diff options
Diffstat (limited to 'drivers/media/platform')
81 files changed, 3103 insertions, 2327 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 25eaf61b98b..08de865cc39 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -36,7 +36,7 @@ source "drivers/media/platform/blackfin/Kconfig" config VIDEO_SH_VOU tristate "SuperH VOU video output driver" depends on MEDIA_CAMERA_SUPPORT - depends on VIDEO_DEV && ARCH_SHMOBILE + depends on VIDEO_DEV && ARCH_SHMOBILE && I2C select VIDEOBUF_DMA_CONTIG help Support for the Video Output Unit (VOU) on SuperH SoCs. diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c index 0e55b087076..7f838c681ce 100644 --- a/drivers/media/platform/blackfin/bfin_capture.c +++ b/drivers/media/platform/blackfin/bfin_capture.c @@ -32,7 +32,6 @@ #include <linux/time.h> #include <linux/types.h> -#include <media/v4l2-chip-ident.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -649,18 +648,30 @@ static int bcap_s_std(struct file *file, void *priv, v4l2_std_id std) return 0; } -static int bcap_g_dv_timings(struct file *file, void *priv, +static int bcap_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + return v4l2_subdev_call(bcap_dev->sd, video, + enum_dv_timings, timings); +} + +static int bcap_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { struct bcap_device *bcap_dev = video_drvdata(file); - int ret; - ret = v4l2_subdev_call(bcap_dev->sd, video, - g_dv_timings, timings); - if (ret < 0) - return ret; + return v4l2_subdev_call(bcap_dev->sd, video, + query_dv_timings, timings); +} - bcap_dev->dv_timings = *timings; +static int bcap_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct bcap_device *bcap_dev = video_drvdata(file); + + *timings = bcap_dev->dv_timings; return 0; } @@ -864,41 +875,6 @@ static int bcap_s_parm(struct file *file, void *fh, return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a); } -static int bcap_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && - chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) - return -EINVAL; - - return v4l2_subdev_call(bcap_dev->sd, core, - g_chip_ident, chip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int bcap_dbg_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - return v4l2_subdev_call(bcap_dev->sd, core, - g_register, reg); -} - -static int bcap_dbg_s_register(struct file *file, void *priv, - const struct v4l2_dbg_register *reg) -{ - struct bcap_device *bcap_dev = video_drvdata(file); - - return v4l2_subdev_call(bcap_dev->sd, core, - s_register, reg); -} -#endif - static int bcap_log_status(struct file *file, void *priv) { struct bcap_device *bcap_dev = video_drvdata(file); @@ -921,6 +897,8 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_g_std = bcap_g_std, .vidioc_s_dv_timings = bcap_s_dv_timings, .vidioc_g_dv_timings = bcap_g_dv_timings, + .vidioc_query_dv_timings = bcap_query_dv_timings, + .vidioc_enum_dv_timings = bcap_enum_dv_timings, .vidioc_reqbufs = bcap_reqbufs, .vidioc_querybuf = bcap_querybuf, .vidioc_qbuf = bcap_qbuf, @@ -929,11 +907,6 @@ static const struct v4l2_ioctl_ops bcap_ioctl_ops = { .vidioc_streamoff = bcap_streamoff, .vidioc_g_parm = bcap_g_parm, .vidioc_s_parm = bcap_s_parm, - .vidioc_g_chip_ident = bcap_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = bcap_dbg_g_register, - .vidioc_s_register = bcap_dbg_s_register, -#endif .vidioc_log_status = bcap_log_status, }; @@ -960,7 +933,7 @@ static int bcap_probe(struct platform_device *pdev) int ret; config = pdev->dev.platform_data; - if (!config) { + if (!config || !config->num_inputs) { v4l2_err(pdev->dev.driver, "Unable to get board config\n"); return -ENODEV; } @@ -1031,7 +1004,9 @@ static int bcap_probe(struct platform_device *pdev) q->mem_ops = &vb2_dma_contig_memops; q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - vb2_queue_init(q); + ret = vb2_queue_init(q); + if (ret) + goto err_free_handler; mutex_init(&bcap_dev->mutex); init_completion(&bcap_dev->comp); @@ -1067,11 +1042,6 @@ static int bcap_probe(struct platform_device *pdev) NULL); if (bcap_dev->sd) { int i; - if (!config->num_inputs) { - v4l2_err(&bcap_dev->v4l2_dev, - "Unable to work without input\n"); - goto err_unreg_vdev; - } /* update tvnorms from the sub devices */ for (i = 0; i < config->num_inputs; i++) @@ -1079,6 +1049,7 @@ static int bcap_probe(struct platform_device *pdev) } else { v4l2_err(&bcap_dev->v4l2_dev, "Unable to register sub device\n"); + ret = -ENODEV; goto err_unreg_vdev; } diff --git a/drivers/media/platform/blackfin/ppi.c b/drivers/media/platform/blackfin/ppi.c index 01b5b501347..15e9c2bac2b 100644 --- a/drivers/media/platform/blackfin/ppi.c +++ b/drivers/media/platform/blackfin/ppi.c @@ -266,6 +266,18 @@ static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params) bfin_write32(®->vcnt, params->height); if (params->int_mask) bfin_write32(®->imsk, params->int_mask & 0xFF); + if (ppi->ppi_control & PORT_DIR) { + u32 hsync_width, vsync_width, vsync_period; + + hsync_width = params->hsync + * params->bpp / params->dlen; + vsync_width = params->vsync * samples_per_line; + vsync_period = samples_per_line * params->frame; + bfin_write32(®->fs1_wlhb, hsync_width); + bfin_write32(®->fs1_paspl, samples_per_line); + bfin_write32(®->fs2_wlvb, vsync_width); + bfin_write32(®->fs2_palpf, vsync_period); + } break; } default: diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c index 9d1481a60bd..df4ada880e4 100644 --- a/drivers/media/platform/coda.c +++ b/drivers/media/platform/coda.c @@ -49,16 +49,14 @@ #define CODA_MAX_FRAMEBUFFERS 2 -#define MAX_W 720 -#define MAX_H 576 -#define CODA_MAX_FRAME_SIZE 0x90000 +#define MAX_W 8192 +#define MAX_H 8192 +#define CODA_MAX_FRAME_SIZE 0x100000 #define FMO_SLICE_SAVE_BUF_SIZE (32) #define CODA_DEFAULT_GAMMA 4096 #define MIN_W 176 #define MIN_H 144 -#define MAX_W 720 -#define MAX_H 576 #define S_ALIGN 1 /* multiple of 2 */ #define W_ALIGN 1 /* multiple of 2 */ @@ -67,7 +65,7 @@ #define fh_to_ctx(__fh) container_of(__fh, struct coda_ctx, fh) static int coda_debug; -module_param(coda_debug, int, 0); +module_param(coda_debug, int, 0644); MODULE_PARM_DESC(coda_debug, "Debug level (0-1)"); enum { @@ -75,11 +73,6 @@ enum { V4L2_M2M_DST = 1, }; -enum coda_fmt_type { - CODA_FMT_ENC, - CODA_FMT_RAW, -}; - enum coda_inst_type { CODA_INST_ENCODER, CODA_INST_DECODER, @@ -93,14 +86,21 @@ enum coda_product { struct coda_fmt { char *name; u32 fourcc; - enum coda_fmt_type type; +}; + +struct coda_codec { + u32 mode; + u32 src_fourcc; + u32 dst_fourcc; + u32 max_w; + u32 max_h; }; struct coda_devtype { char *firmware; enum coda_product product; - struct coda_fmt *formats; - unsigned int num_formats; + struct coda_codec *codecs; + unsigned int num_codecs; size_t workbuf_size; }; @@ -109,7 +109,7 @@ struct coda_q_data { unsigned int width; unsigned int height; unsigned int sizeimage; - struct coda_fmt *fmt; + unsigned int fourcc; }; struct coda_aux_buf { @@ -137,12 +137,12 @@ struct coda_dev { spinlock_t irqlock; struct mutex dev_mutex; + struct mutex coda_mutex; struct v4l2_m2m_dev *m2m_dev; struct vb2_alloc_ctx *alloc_ctx; struct list_head instances; unsigned long instance_mask; struct delayed_work timeout; - struct completion done; }; struct coda_params { @@ -164,11 +164,12 @@ struct coda_ctx { struct coda_dev *dev; struct list_head list; int aborting; - int rawstreamon; - int compstreamon; + int streamon_out; + int streamon_cap; u32 isequence; struct coda_q_data q_data[2]; enum coda_inst_type inst_type; + struct coda_codec *codec; enum v4l2_colorspace colorspace; struct coda_params params; struct v4l2_m2m_ctx *m2m_ctx; @@ -257,62 +258,89 @@ static struct coda_q_data *get_q_data(struct coda_ctx *ctx, } /* - * Add one array of supported formats for each version of Coda: - * i.MX27 -> codadx6 - * i.MX51 -> coda7 - * i.MX6 -> coda960 + * Array of all formats supported by any version of Coda: */ -static struct coda_fmt codadx6_formats[] = { +static struct coda_fmt coda_formats[] = { { - .name = "YUV 4:2:0 Planar", + .name = "YUV 4:2:0 Planar, YCbCr", .fourcc = V4L2_PIX_FMT_YUV420, - .type = CODA_FMT_RAW, - }, - { - .name = "H264 Encoded Stream", - .fourcc = V4L2_PIX_FMT_H264, - .type = CODA_FMT_ENC, }, { - .name = "MPEG4 Encoded Stream", - .fourcc = V4L2_PIX_FMT_MPEG4, - .type = CODA_FMT_ENC, - }, -}; - -static struct coda_fmt coda7_formats[] = { - { - .name = "YUV 4:2:0 Planar", - .fourcc = V4L2_PIX_FMT_YUV420, - .type = CODA_FMT_RAW, + .name = "YUV 4:2:0 Planar, YCrCb", + .fourcc = V4L2_PIX_FMT_YVU420, }, { .name = "H264 Encoded Stream", .fourcc = V4L2_PIX_FMT_H264, - .type = CODA_FMT_ENC, }, { .name = "MPEG4 Encoded Stream", .fourcc = V4L2_PIX_FMT_MPEG4, - .type = CODA_FMT_ENC, }, }; -static struct coda_fmt *find_format(struct coda_dev *dev, struct v4l2_format *f) +#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \ + { mode, src_fourcc, dst_fourcc, max_w, max_h } + +/* + * Arrays of codecs supported by each given version of Coda: + * i.MX27 -> codadx6 + * i.MX5x -> coda7 + * i.MX6 -> coda960 + * Use V4L2_PIX_FMT_YUV420 as placeholder for all supported YUV 4:2:0 variants + */ +static struct coda_codec codadx6_codecs[] = { + CODA_CODEC(CODADX6_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 720, 576), + CODA_CODEC(CODADX6_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 720, 576), +}; + +static struct coda_codec coda7_codecs[] = { + CODA_CODEC(CODA7_MODE_ENCODE_H264, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_H264, 1280, 720), + CODA_CODEC(CODA7_MODE_ENCODE_MP4, V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_MPEG4, 1280, 720), +}; + +static bool coda_format_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_YVU420: + return true; + default: + return false; + } +} + +/* + * Normalize all supported YUV 4:2:0 formats to the value used in the codec + * tables. + */ +static u32 coda_format_normalize_yuv(u32 fourcc) +{ + return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc; +} + +static struct coda_codec *coda_find_codec(struct coda_dev *dev, int src_fourcc, + int dst_fourcc) { - struct coda_fmt *formats = dev->devtype->formats; - int num_formats = dev->devtype->num_formats; - unsigned int k; + struct coda_codec *codecs = dev->devtype->codecs; + int num_codecs = dev->devtype->num_codecs; + int k; + + src_fourcc = coda_format_normalize_yuv(src_fourcc); + dst_fourcc = coda_format_normalize_yuv(dst_fourcc); + if (src_fourcc == dst_fourcc) + return NULL; - for (k = 0; k < num_formats; k++) { - if (formats[k].fourcc == f->fmt.pix.pixelformat) + for (k = 0; k < num_codecs; k++) { + if (codecs[k].src_fourcc == src_fourcc && + codecs[k].dst_fourcc == dst_fourcc) break; } - if (k == num_formats) + if (k == num_codecs) return NULL; - return &formats[k]; + return &codecs[k]; } /* @@ -323,7 +351,7 @@ static int vidioc_querycap(struct file *file, void *priv, { strlcpy(cap->driver, CODA_NAME, sizeof(cap->driver)); strlcpy(cap->card, CODA_NAME, sizeof(cap->card)); - strlcpy(cap->bus_info, CODA_NAME, sizeof(cap->bus_info)); + strlcpy(cap->bus_info, "platform:" CODA_NAME, sizeof(cap->bus_info)); /* * This is only a mem-to-mem video device. The capture and output * device capability flags are left only for backward compatibility @@ -337,17 +365,34 @@ static int vidioc_querycap(struct file *file, void *priv, } static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, - enum coda_fmt_type type) + enum v4l2_buf_type type) { struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_dev *dev = ctx->dev; - struct coda_fmt *formats = dev->devtype->formats; + struct coda_codec *codecs = ctx->dev->devtype->codecs; + struct coda_fmt *formats = coda_formats; struct coda_fmt *fmt; - int num_formats = dev->devtype->num_formats; - int i, num = 0; + int num_codecs = ctx->dev->devtype->num_codecs; + int num_formats = ARRAY_SIZE(coda_formats); + int i, k, num = 0; for (i = 0; i < num_formats; i++) { - if (formats[i].type == type) { + /* Both uncompressed formats are always supported */ + if (coda_format_is_yuv(formats[i].fourcc)) { + if (num == f->index) + break; + ++num; + continue; + } + /* Compressed formats may be supported, check the codec list */ + for (k = 0; k < num_codecs; k++) { + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE && + formats[i].fourcc == codecs[k].dst_fourcc) + break; + if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT && + formats[i].fourcc == codecs[k].src_fourcc) + break; + } + if (k < num_codecs) { if (num == f->index) break; ++num; @@ -368,13 +413,13 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f, static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return enum_fmt(priv, f, CODA_FMT_ENC); + return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE); } static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - return enum_fmt(priv, f, CODA_FMT_RAW); + return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT); } static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) @@ -390,10 +435,10 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) q_data = get_q_data(ctx, f->type); f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.pixelformat = q_data->fmt->fourcc; + f->fmt.pix.pixelformat = q_data->fourcc; f->fmt.pix.width = q_data->width; f->fmt.pix.height = q_data->height; - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + if (coda_format_is_yuv(f->fmt.pix.pixelformat)) f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2); else /* encoded formats h.264/mpeg4 */ f->fmt.pix.bytesperline = 0; @@ -404,8 +449,9 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f) return 0; } -static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f) +static int vidioc_try_fmt(struct coda_codec *codec, struct v4l2_format *f) { + unsigned int max_w, max_h; enum v4l2_field field; field = f->fmt.pix.field; @@ -418,12 +464,21 @@ static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f) * if any of the dimensions is unsupported */ f->fmt.pix.field = field; - if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { - v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W, - W_ALIGN, &f->fmt.pix.height, - MIN_H, MAX_H, H_ALIGN, S_ALIGN); - f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 2); - f->fmt.pix.sizeimage = f->fmt.pix.width * + if (codec) { + max_w = codec->max_w; + max_h = codec->max_h; + } else { + max_w = MAX_W; + max_h = MAX_H; + } + v4l_bound_align_image(&f->fmt.pix.width, MIN_W, max_w, + W_ALIGN, &f->fmt.pix.height, + MIN_H, max_h, H_ALIGN, S_ALIGN); + + if (coda_format_is_yuv(f->fmt.pix.pixelformat)) { + /* Frame stride must be multiple of 8 */ + f->fmt.pix.bytesperline = round_up(f->fmt.pix.width, 8); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height * 3 / 2; } else { /*encoded formats h.264/mpeg4 */ f->fmt.pix.bytesperline = 0; @@ -436,57 +491,38 @@ static int vidioc_try_fmt(struct coda_dev *dev, struct v4l2_format *f) static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - int ret; - struct coda_fmt *fmt; struct coda_ctx *ctx = fh_to_ctx(priv); + struct coda_codec *codec = NULL; - fmt = find_format(ctx->dev, f); - /* - * Since decoding support is not implemented yet do not allow - * CODA_FMT_RAW formats in the capture interface. - */ - if (!fmt || !(fmt->type == CODA_FMT_ENC)) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_H264; + /* Determine codec by the encoded format */ + codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420, + f->fmt.pix.pixelformat); f->fmt.pix.colorspace = ctx->colorspace; - ret = vidioc_try_fmt(ctx->dev, f); - if (ret < 0) - return ret; - - return 0; + return vidioc_try_fmt(codec, f); } static int vidioc_try_fmt_vid_out(struct file *file, void *priv, struct v4l2_format *f) { struct coda_ctx *ctx = fh_to_ctx(priv); - struct coda_fmt *fmt; - int ret; + struct coda_codec *codec; - fmt = find_format(ctx->dev, f); - /* - * Since decoding support is not implemented yet do not allow - * CODA_FMT formats in the capture interface. - */ - if (!fmt || !(fmt->type == CODA_FMT_RAW)) - f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; + /* Determine codec by encoded format, returns NULL if raw or invalid */ + codec = coda_find_codec(ctx->dev, f->fmt.pix.pixelformat, + V4L2_PIX_FMT_YUV420); if (!f->fmt.pix.colorspace) f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709; - ret = vidioc_try_fmt(ctx->dev, f); - if (ret < 0) - return ret; - - return 0; + return vidioc_try_fmt(codec, f); } static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) { struct coda_q_data *q_data; struct vb2_queue *vq; - int ret; vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); if (!vq) @@ -501,18 +537,14 @@ static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) return -EBUSY; } - ret = vidioc_try_fmt(ctx->dev, f); - if (ret) - return ret; - - q_data->fmt = find_format(ctx->dev, f); + q_data->fourcc = f->fmt.pix.pixelformat; q_data->width = f->fmt.pix.width; q_data->height = f->fmt.pix.height; q_data->sizeimage = f->fmt.pix.sizeimage; v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Setting format for type %d, wxh: %dx%d, fmt: %d\n", - f->type, q_data->width, q_data->height, q_data->fmt->fourcc); + f->type, q_data->width, q_data->height, q_data->fourcc); return 0; } @@ -520,13 +552,14 @@ static int vidioc_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f) static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { + struct coda_ctx *ctx = fh_to_ctx(priv); int ret; ret = vidioc_try_fmt_vid_cap(file, priv, f); if (ret) return ret; - return vidioc_s_fmt(fh_to_ctx(priv), f); + return vidioc_s_fmt(ctx, f); } static int vidioc_s_fmt_vid_out(struct file *file, void *priv, @@ -569,6 +602,14 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); } +static int vidioc_expbuf(struct file *file, void *priv, + struct v4l2_exportbuffer *eb) +{ + struct coda_ctx *ctx = fh_to_ctx(priv); + + return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb); +} + static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) { struct coda_ctx *ctx = fh_to_ctx(priv); @@ -617,6 +658,7 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, + .vidioc_expbuf = vidioc_expbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_create_bufs = vidioc_create_bufs, @@ -639,11 +681,13 @@ static void coda_device_run(void *m2m_priv) u32 pic_stream_buffer_addr, pic_stream_buffer_size; u32 dst_fourcc; + mutex_lock(&dev->coda_mutex); + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); - dst_fourcc = q_data_dst->fmt->fourcc; + dst_fourcc = q_data_dst->fourcc; src_buf->v4l2_buf.sequence = ctx->isequence; dst_buf->v4l2_buf.sequence = ctx->isequence; @@ -725,9 +769,20 @@ static void coda_device_run(void *m2m_priv) picture_y = vb2_dma_contig_plane_dma_addr(src_buf, 0); - picture_cb = picture_y + q_data_src->width * q_data_src->height; - picture_cr = picture_cb + q_data_src->width / 2 * - q_data_src->height / 2; + switch (q_data_src->fourcc) { + case V4L2_PIX_FMT_YVU420: + /* Switch Cb and Cr for YVU420 format */ + picture_cr = picture_y + q_data_src->width * q_data_src->height; + picture_cb = picture_cr + q_data_src->width / 2 * + q_data_src->height / 2; + break; + case V4L2_PIX_FMT_YUV420: + default: + picture_cb = picture_y + q_data_src->width * q_data_src->height; + picture_cr = picture_cb + q_data_src->width / 2 * + q_data_src->height / 2; + break; + } coda_write(dev, picture_y, CODA_CMD_ENC_PIC_SRC_ADDR_Y); coda_write(dev, picture_cb, CODA_CMD_ENC_PIC_SRC_ADDR_CB); @@ -748,7 +803,6 @@ static void coda_device_run(void *m2m_priv) /* 1 second timeout in case CODA locks up */ schedule_delayed_work(&dev->timeout, HZ); - INIT_COMPLETION(dev->done); coda_command_async(ctx, CODA_COMMAND_PIC_RUN); } @@ -767,6 +821,12 @@ static int coda_job_ready(void *m2m_priv) return 0; } + if (ctx->aborting) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "not ready: aborting\n"); + return 0; + } + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "job ready\n"); return 1; @@ -775,14 +835,11 @@ static int coda_job_ready(void *m2m_priv) static void coda_job_abort(void *priv) { struct coda_ctx *ctx = priv; - struct coda_dev *dev = ctx->dev; ctx->aborting = 1; v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "Aborting task\n"); - - v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); } static void coda_lock(void *m2m_priv) @@ -809,7 +866,12 @@ static struct v4l2_m2m_ops coda_m2m_ops = { static void set_default_params(struct coda_ctx *ctx) { - struct coda_dev *dev = ctx->dev; + int max_w; + int max_h; + + ctx->codec = &ctx->dev->devtype->codecs[0]; + max_w = ctx->codec->max_w; + max_h = ctx->codec->max_h; ctx->params.codec_mode = CODA_MODE_INVALID; ctx->colorspace = V4L2_COLORSPACE_REC709; @@ -817,13 +879,13 @@ static void set_default_params(struct coda_ctx *ctx) ctx->aborting = 0; /* Default formats for output and input queues */ - ctx->q_data[V4L2_M2M_SRC].fmt = &dev->devtype->formats[0]; - ctx->q_data[V4L2_M2M_DST].fmt = &dev->devtype->formats[1]; - ctx->q_data[V4L2_M2M_SRC].width = MAX_W; - ctx->q_data[V4L2_M2M_SRC].height = MAX_H; - ctx->q_data[V4L2_M2M_SRC].sizeimage = (MAX_W * MAX_H * 3) / 2; - ctx->q_data[V4L2_M2M_DST].width = MAX_W; - ctx->q_data[V4L2_M2M_DST].height = MAX_H; + ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc; + ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc; + ctx->q_data[V4L2_M2M_SRC].width = max_w; + ctx->q_data[V4L2_M2M_SRC].height = max_h; + ctx->q_data[V4L2_M2M_SRC].sizeimage = (max_w * max_h * 3) / 2; + ctx->q_data[V4L2_M2M_DST].width = max_w; + ctx->q_data[V4L2_M2M_DST].height = max_h; ctx->q_data[V4L2_M2M_DST].sizeimage = CODA_MAX_FRAME_SIZE; } @@ -868,8 +930,6 @@ static int coda_buf_prepare(struct vb2_buffer *vb) return -EINVAL; } - vb2_set_plane_payload(vb, 0, q_data->sizeimage); - return 0; } @@ -906,21 +966,34 @@ static void coda_free_framebuffers(struct coda_ctx *ctx) } } +static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value) +{ + struct coda_dev *dev = ctx->dev; + u32 *p = ctx->parabuf.vaddr; + + if (dev->devtype->product == CODA_DX6) + p[index] = value; + else + p[index ^ 1] = value; +} + static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_data, u32 fourcc) { struct coda_dev *dev = ctx->dev; int height = q_data->height; - int width = q_data->width; - u32 *p; + dma_addr_t paddr; + int ysize; int i; + ysize = round_up(q_data->width, 8) * height; + /* Allocate frame buffers */ ctx->num_internal_frames = CODA_MAX_FRAMEBUFFERS; for (i = 0; i < ctx->num_internal_frames; i++) { ctx->internal_frames[i].size = q_data->sizeimage; if (fourcc == V4L2_PIX_FMT_H264 && dev->devtype->product != CODA_DX6) - ctx->internal_frames[i].size += width / 2 * height / 2; + ctx->internal_frames[i].size += ysize/4; ctx->internal_frames[i].vaddr = dma_alloc_coherent( &dev->plat_dev->dev, ctx->internal_frames[i].size, &ctx->internal_frames[i].paddr, GFP_KERNEL); @@ -931,32 +1004,14 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d } /* Register frame buffers in the parameter buffer */ - p = ctx->parabuf.vaddr; + for (i = 0; i < ctx->num_internal_frames; i++) { + paddr = ctx->internal_frames[i].paddr; + coda_parabuf_write(ctx, i * 3 + 0, paddr); /* Y */ + coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize); /* Cb */ + coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize/4); /* Cr */ - if (dev->devtype->product == CODA_DX6) { - for (i = 0; i < ctx->num_internal_frames; i++) { - p[i * 3] = ctx->internal_frames[i].paddr; /* Y */ - p[i * 3 + 1] = p[i * 3] + width * height; /* Cb */ - p[i * 3 + 2] = p[i * 3 + 1] + width / 2 * height / 2; /* Cr */ - } - } else { - for (i = 0; i < ctx->num_internal_frames; i += 2) { - p[i * 3 + 1] = ctx->internal_frames[i].paddr; /* Y */ - p[i * 3] = p[i * 3 + 1] + width * height; /* Cb */ - p[i * 3 + 3] = p[i * 3] + (width / 2) * (height / 2); /* Cr */ - - if (fourcc == V4L2_PIX_FMT_H264) - p[96 + i + 1] = p[i * 3 + 3] + (width / 2) * (height / 2); - - if (i + 1 < ctx->num_internal_frames) { - p[i * 3 + 2] = ctx->internal_frames[i+1].paddr; /* Y */ - p[i * 3 + 5] = p[i * 3 + 2] + width * height ; /* Cb */ - p[i * 3 + 4] = p[i * 3 + 5] + (width / 2) * (height / 2); /* Cr */ - - if (fourcc == V4L2_PIX_FMT_H264) - p[96 + i] = p[i * 3 + 4] + (width / 2) * (height / 2); - } - } + if (dev->devtype->product != CODA_DX6 && fourcc == V4L2_PIX_FMT_H264) + coda_parabuf_write(ctx, 96 + i, ctx->internal_frames[i].paddr + ysize + ysize/4 + ysize/4); } return 0; @@ -980,6 +1035,28 @@ static int coda_h264_padding(int size, char *p) return nal_size; } +static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf, + int header_code, u8 *header, int *size) +{ + struct coda_dev *dev = ctx->dev; + int ret; + + coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), + CODA_CMD_ENC_HEADER_BB_START); + coda_write(dev, vb2_plane_size(buf, 0), CODA_CMD_ENC_HEADER_BB_SIZE); + coda_write(dev, header_code, CODA_CMD_ENC_HEADER_CODE); + ret = coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); + return ret; + } + *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - + coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); + memcpy(header, vb2_plane_vaddr(buf, 0), *size); + + return 0; +} + static int coda_start_streaming(struct vb2_queue *q, unsigned int count) { struct coda_ctx *ctx = vb2_get_drv_priv(q); @@ -990,43 +1067,38 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) struct vb2_buffer *buf; u32 dst_fourcc; u32 value; - int ret; + int ret = 0; if (count < 1) return -EINVAL; if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) - ctx->rawstreamon = 1; + ctx->streamon_out = 1; else - ctx->compstreamon = 1; + ctx->streamon_cap = 1; + + q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); + if (ctx->streamon_out) { + if (coda_format_is_yuv(q_data_src->fourcc)) + ctx->inst_type = CODA_INST_ENCODER; + else + ctx->inst_type = CODA_INST_DECODER; + } /* Don't start the coda unless both queues are on */ - if (!(ctx->rawstreamon & ctx->compstreamon)) + if (!(ctx->streamon_out & ctx->streamon_cap)) return 0; - if (coda_isbusy(dev)) - if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) - return -EBUSY; - ctx->gopcounter = ctx->params.gop_size - 1; - - q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0); q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); bitstream_size = q_data_dst->sizeimage; - dst_fourcc = q_data_dst->fmt->fourcc; - - /* Find out whether coda must encode or decode */ - if (q_data_src->fmt->type == CODA_FMT_RAW && - q_data_dst->fmt->type == CODA_FMT_ENC) { - ctx->inst_type = CODA_INST_ENCODER; - } else if (q_data_src->fmt->type == CODA_FMT_ENC && - q_data_dst->fmt->type == CODA_FMT_RAW) { - ctx->inst_type = CODA_INST_DECODER; - v4l2_err(v4l2_dev, "decoding not supported.\n"); - return -EINVAL; - } else { + dst_fourcc = q_data_dst->fourcc; + + ctx->codec = coda_find_codec(ctx->dev, q_data_src->fourcc, + q_data_dst->fourcc); + if (!ctx->codec) { v4l2_err(v4l2_dev, "couldn't tell instance type.\n"); return -EINVAL; } @@ -1035,6 +1107,9 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) v4l2_err(v4l2_dev, "coda is not initialized.\n"); return -EFAULT; } + + mutex_lock(&dev->coda_mutex); + coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR); coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->idx)); coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->idx)); @@ -1057,38 +1132,31 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) switch (dev->devtype->product) { case CODA_DX6: value = (q_data_src->width & CODADX6_PICWIDTH_MASK) << CODADX6_PICWIDTH_OFFSET; + value |= (q_data_src->height & CODADX6_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; break; default: value = (q_data_src->width & CODA7_PICWIDTH_MASK) << CODA7_PICWIDTH_OFFSET; + value |= (q_data_src->height & CODA7_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; } - value |= (q_data_src->height & CODA_PICHEIGHT_MASK) << CODA_PICHEIGHT_OFFSET; coda_write(dev, value, CODA_CMD_ENC_SEQ_SRC_SIZE); coda_write(dev, ctx->params.framerate, CODA_CMD_ENC_SEQ_SRC_F_RATE); + ctx->params.codec_mode = ctx->codec->mode; switch (dst_fourcc) { case V4L2_PIX_FMT_MPEG4: - if (dev->devtype->product == CODA_DX6) - ctx->params.codec_mode = CODADX6_MODE_ENCODE_MP4; - else - ctx->params.codec_mode = CODA7_MODE_ENCODE_MP4; - coda_write(dev, CODA_STD_MPEG4, CODA_CMD_ENC_SEQ_COD_STD); coda_write(dev, 0, CODA_CMD_ENC_SEQ_MP4_PARA); break; case V4L2_PIX_FMT_H264: - if (dev->devtype->product == CODA_DX6) - ctx->params.codec_mode = CODADX6_MODE_ENCODE_H264; - else - ctx->params.codec_mode = CODA7_MODE_ENCODE_H264; - coda_write(dev, CODA_STD_H264, CODA_CMD_ENC_SEQ_COD_STD); coda_write(dev, 0, CODA_CMD_ENC_SEQ_264_PARA); break; default: v4l2_err(v4l2_dev, "dst format (0x%08x) invalid.\n", dst_fourcc); - return -EINVAL; + ret = -EINVAL; + goto out; } switch (ctx->params.slice_mode) { @@ -1129,8 +1197,14 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) value = (CODA_DEFAULT_GAMMA & CODA_GAMMA_MASK) << CODA_GAMMA_OFFSET; coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_GAMMA); - value = (CODA_DEFAULT_GAMMA > 0) << CODA_OPTION_GAMMA_OFFSET; - value |= (0 & CODA_OPTION_SLICEREPORT_MASK) << CODA_OPTION_SLICEREPORT_OFFSET; + if (CODA_DEFAULT_GAMMA > 0) { + if (dev->devtype->product == CODA_DX6) + value = 1 << CODADX6_OPTION_GAMMA_OFFSET; + else + value = 1 << CODA7_OPTION_GAMMA_OFFSET; + } else { + value = 0; + } coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION); if (dst_fourcc == V4L2_PIX_FMT_H264) { @@ -1145,17 +1219,23 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) } } - if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) { + ret = coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT); + if (ret < 0) { v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n"); - return -ETIMEDOUT; + goto out; } - if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) - return -EFAULT; + if (coda_read(dev, CODA_RET_ENC_SEQ_SUCCESS) == 0) { + v4l2_err(v4l2_dev, "CODA_COMMAND_SEQ_INIT failed\n"); + ret = -EFAULT; + goto out; + } ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc); - if (ret < 0) - return ret; + if (ret < 0) { + v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); + goto out; + } coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); coda_write(dev, round_up(q_data_src->width, 8), CODA_CMD_SET_FRAME_BUF_STRIDE); @@ -1167,9 +1247,10 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) coda_write(dev, dev->iram_paddr + 68 * 1024, CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR); coda_write(dev, 0x0, CODA7_CMD_SET_FRAME_AXI_OVL_ADDR); } - if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) { + ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF); + if (ret < 0) { v4l2_err(v4l2_dev, "CODA_COMMAND_SET_FRAME_BUF timeout\n"); - return -ETIMEDOUT; + goto out; } /* Save stream headers */ @@ -1180,33 +1261,22 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) * Get SPS in the first frame and copy it to an * intermediate buffer. */ - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, CODA_HEADER_H264_SPS, CODA_CMD_ENC_HEADER_CODE); - if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { - v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return -ETIMEDOUT; - } - ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0), - ctx->vpu_header_size[0]); + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_SPS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; /* * Get PPS in the first frame and copy it to an * intermediate buffer. */ - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, CODA_HEADER_H264_PPS, CODA_CMD_ENC_HEADER_CODE); - if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { - v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return -ETIMEDOUT; - } - ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), - ctx->vpu_header_size[1]); + ret = coda_encode_header(ctx, buf, CODA_HEADER_H264_PPS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + /* * Length of H.264 headers is variable and thus it might not be * aligned for the coda to append the encoded frame. In that is @@ -1222,48 +1292,32 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) * Get VOS in the first frame and copy it to an * intermediate buffer */ - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, CODA_HEADER_MP4V_VOS, CODA_CMD_ENC_HEADER_CODE); - if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { - v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n"); - return -ETIMEDOUT; - } - ctx->vpu_header_size[0] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - memcpy(&ctx->vpu_header[0][0], vb2_plane_vaddr(buf, 0), - ctx->vpu_header_size[0]); - - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, CODA_HEADER_MP4V_VIS, CODA_CMD_ENC_HEADER_CODE); - if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { - v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n"); - return -ETIMEDOUT; - } - ctx->vpu_header_size[1] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - memcpy(&ctx->vpu_header[1][0], vb2_plane_vaddr(buf, 0), - ctx->vpu_header_size[1]); - - coda_write(dev, vb2_dma_contig_plane_dma_addr(buf, 0), CODA_CMD_ENC_HEADER_BB_START); - coda_write(dev, bitstream_size, CODA_CMD_ENC_HEADER_BB_SIZE); - coda_write(dev, CODA_HEADER_MP4V_VOL, CODA_CMD_ENC_HEADER_CODE); - if (coda_command_sync(ctx, CODA_COMMAND_ENCODE_HEADER)) { - v4l2_err(v4l2_dev, "CODA_COMMAND_ENCODE_HEADER failed\n"); - return -ETIMEDOUT; - } - ctx->vpu_header_size[2] = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) - - coda_read(dev, CODA_CMD_ENC_HEADER_BB_START); - memcpy(&ctx->vpu_header[2][0], vb2_plane_vaddr(buf, 0), - ctx->vpu_header_size[2]); + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOS, + &ctx->vpu_header[0][0], + &ctx->vpu_header_size[0]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VIS, + &ctx->vpu_header[1][0], + &ctx->vpu_header_size[1]); + if (ret < 0) + goto out; + + ret = coda_encode_header(ctx, buf, CODA_HEADER_MP4V_VOL, + &ctx->vpu_header[2][0], + &ctx->vpu_header_size[2]); + if (ret < 0) + goto out; break; default: /* No more formats need to save headers at the moment */ break; } - return 0; +out: + mutex_unlock(&dev->coda_mutex); + return ret; } static int coda_stop_streaming(struct vb2_queue *q) @@ -1274,26 +1328,20 @@ static int coda_stop_streaming(struct vb2_queue *q) if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%s: output\n", __func__); - ctx->rawstreamon = 0; + ctx->streamon_out = 0; } else { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "%s: capture\n", __func__); - ctx->compstreamon = 0; + ctx->streamon_cap = 0; } /* Don't stop the coda unless both queues are off */ - if (ctx->rawstreamon || ctx->compstreamon) + if (ctx->streamon_out || ctx->streamon_cap) return 0; - if (coda_isbusy(dev)) { - if (wait_for_completion_interruptible_timeout(&dev->done, HZ) <= 0) { - v4l2_warn(&dev->v4l2_dev, - "%s: timeout, sending SEQ_END anyway\n", __func__); - } - } - cancel_delayed_work(&dev->timeout); + mutex_lock(&dev->coda_mutex); v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s: sent command 'SEQ_END' to coda\n", __func__); if (coda_command_sync(ctx, CODA_COMMAND_SEQ_END)) { @@ -1301,6 +1349,7 @@ static int coda_stop_streaming(struct vb2_queue *q) "CODA_COMMAND_SEQ_END failed\n"); return -ETIMEDOUT; } + mutex_unlock(&dev->coda_mutex); coda_free_framebuffers(ctx); @@ -1431,7 +1480,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, int ret; src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &coda_qops; @@ -1443,7 +1492,7 @@ static int coda_queue_init(void *priv, struct vb2_queue *src_vq, return ret; dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &coda_qops; @@ -1484,7 +1533,7 @@ static int coda_open(struct file *file) ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &coda_queue_init); if (IS_ERR(ctx->m2m_ctx)) { - int ret = PTR_ERR(ctx->m2m_ctx); + ret = PTR_ERR(ctx->m2m_ctx); v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n", __func__, ret); @@ -1596,12 +1645,14 @@ static irqreturn_t coda_irq_handler(int irq, void *data) ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); if (ctx == NULL) { v4l2_err(&dev->v4l2_dev, "Instance released before the end of transaction\n"); + mutex_unlock(&dev->coda_mutex); return IRQ_HANDLED; } if (ctx->aborting) { v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "task has been aborted\n"); + mutex_unlock(&dev->coda_mutex); return IRQ_HANDLED; } @@ -1611,8 +1662,6 @@ static irqreturn_t coda_irq_handler(int irq, void *data) return IRQ_NONE; } - complete(&dev->done); - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); @@ -1660,6 +1709,8 @@ static irqreturn_t coda_irq_handler(int irq, void *data) (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ? "KEYFRAME" : "PFRAME"); + mutex_unlock(&dev->coda_mutex); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); return IRQ_HANDLED; @@ -1671,12 +1722,7 @@ static void coda_timeout(struct work_struct *work) struct coda_dev *dev = container_of(to_delayed_work(work), struct coda_dev, timeout); - if (completion_done(&dev->done)) - return; - - complete(&dev->done); - - v4l2_err(&dev->v4l2_dev, "CODA PIC_RUN timeout, stopping all streams\n"); + dev_err(&dev->plat_dev->dev, "CODA PIC_RUN timeout, stopping all streams\n"); mutex_lock(&dev->dev_mutex); list_for_each_entry(ctx, &dev->instances, list) { @@ -1684,6 +1730,10 @@ static void coda_timeout(struct work_struct *work) v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); } mutex_unlock(&dev->dev_mutex); + + mutex_unlock(&dev->coda_mutex); + ctx = v4l2_m2m_get_curr_priv(dev->m2m_dev); + v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx); } static u32 coda_supported_firmwares[] = { @@ -1748,6 +1798,10 @@ static int coda_hw_init(struct coda_dev *dev) } } + /* Clear registers */ + for (i = 0; i < 64; i++) + coda_write(dev, 0, CODA_REG_BIT_CODE_BUF_ADDR + i * 4); + /* Tell the BIT where to find everything it needs */ coda_write(dev, dev->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR); @@ -1911,16 +1965,16 @@ enum coda_platform { static const struct coda_devtype coda_devdata[] = { [CODA_IMX27] = { - .firmware = "v4l-codadx6-imx27.bin", - .product = CODA_DX6, - .formats = codadx6_formats, - .num_formats = ARRAY_SIZE(codadx6_formats), + .firmware = "v4l-codadx6-imx27.bin", + .product = CODA_DX6, + .codecs = codadx6_codecs, + .num_codecs = ARRAY_SIZE(codadx6_codecs), }, [CODA_IMX53] = { - .firmware = "v4l-coda7541-imx53.bin", - .product = CODA_7541, - .formats = coda7_formats, - .num_formats = ARRAY_SIZE(coda7_formats), + .firmware = "v4l-coda7541-imx53.bin", + .product = CODA_7541, + .codecs = coda7_codecs, + .num_codecs = ARRAY_SIZE(coda7_codecs), }, }; @@ -1962,8 +2016,6 @@ static int coda_probe(struct platform_device *pdev) spin_lock_init(&dev->irqlock); INIT_LIST_HEAD(&dev->instances); INIT_DELAYED_WORK(&dev->timeout, coda_timeout); - init_completion(&dev->done); - complete(&dev->done); dev->plat_dev = pdev; dev->clk_per = devm_clk_get(&pdev->dev, "per"); @@ -1985,17 +2037,9 @@ static int coda_probe(struct platform_device *pdev) return -ENOENT; } - if (devm_request_mem_region(&pdev->dev, res->start, - resource_size(res), CODA_NAME) == NULL) { - dev_err(&pdev->dev, "failed to request memory region\n"); - return -ENOENT; - } - dev->regs_base = devm_ioremap(&pdev->dev, res->start, - resource_size(res)); - if (!dev->regs_base) { - dev_err(&pdev->dev, "failed to ioremap address region\n"); - return -ENOENT; - } + dev->regs_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->regs_base)) + return PTR_ERR(dev->regs_base); /* IRQ */ irq = platform_get_irq(pdev, 0); @@ -2025,6 +2069,7 @@ static int coda_probe(struct platform_device *pdev) return ret; mutex_init(&dev->dev_mutex); + mutex_init(&dev->coda_mutex); pdev_id = of_id ? of_id->data : platform_get_device_id(pdev); diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h index f3f5e43c1ac..ace0bf0a3b9 100644 --- a/drivers/media/platform/coda.h +++ b/drivers/media/platform/coda.h @@ -96,16 +96,12 @@ #define CODA_CMD_ENC_SEQ_BB_START 0x180 #define CODA_CMD_ENC_SEQ_BB_SIZE 0x184 #define CODA_CMD_ENC_SEQ_OPTION 0x188 -#define CODA_OPTION_GAMMA_OFFSET 7 -#define CODA_OPTION_GAMMA_MASK 0x01 +#define CODA7_OPTION_GAMMA_OFFSET 8 +#define CODADX6_OPTION_GAMMA_OFFSET 7 #define CODA_OPTION_LIMITQP_OFFSET 6 -#define CODA_OPTION_LIMITQP_MASK 0x01 #define CODA_OPTION_RCINTRAQP_OFFSET 5 -#define CODA_OPTION_RCINTRAQP_MASK 0x01 #define CODA_OPTION_FMO_OFFSET 4 -#define CODA_OPTION_FMO_MASK 0x01 #define CODA_OPTION_SLICEREPORT_OFFSET 1 -#define CODA_OPTION_SLICEREPORT_MASK 0x01 #define CODA_CMD_ENC_SEQ_COD_STD 0x18c #define CODA_STD_MPEG4 0 #define CODA_STD_H263 1 @@ -117,7 +113,8 @@ #define CODADX6_PICWIDTH_OFFSET 10 #define CODADX6_PICWIDTH_MASK 0x3ff #define CODA_PICHEIGHT_OFFSET 0 -#define CODA_PICHEIGHT_MASK 0x3ff +#define CODADX6_PICHEIGHT_MASK 0x3ff +#define CODA7_PICHEIGHT_MASK 0xffff #define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194 #define CODA_CMD_ENC_SEQ_MP4_PARA 0x198 #define CODA_MP4PARAM_VERID_OFFSET 6 diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c index d0b375cf565..e180ff7282d 100644 --- a/drivers/media/platform/davinci/vpbe_display.c +++ b/drivers/media/platform/davinci/vpbe_display.c @@ -944,7 +944,7 @@ static int vpbe_display_s_fmt(struct file *file, void *priv, cfg->interlaced = vpbe_dev->current_timings.interlaced; if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) - cfg->pixfmt = PIXFMT_YCbCrI; + cfg->pixfmt = PIXFMT_YCBCRI; /* Change of the default pixel format for both video windows */ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { @@ -1593,31 +1593,6 @@ static int vpbe_display_release(struct file *file) return 0; } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vpbe_display_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) -{ - struct v4l2_dbg_match *match = ®->match; - struct vpbe_fh *fh = file->private_data; - struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; - - if (match->type >= 2) { - v4l2_subdev_call(vpbe_dev->venc, - core, - g_register, - reg); - } - - return 0; -} - -static int vpbe_display_s_register(struct file *file, void *priv, - const struct v4l2_dbg_register *reg) -{ - return 0; -} -#endif - /* vpbe capture ioctl operations */ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_querycap = vpbe_display_querycap, @@ -1644,10 +1619,6 @@ static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { .vidioc_s_dv_timings = vpbe_display_s_dv_timings, .vidioc_g_dv_timings = vpbe_display_g_dv_timings, .vidioc_enum_dv_timings = vpbe_display_enum_dv_timings, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vpbe_display_g_register, - .vidioc_s_register = vpbe_display_s_register, -#endif }; static struct v4l2_file_operations vpbe_fops = { diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c index 396a51cbede..6ed82e8b297 100644 --- a/drivers/media/platform/davinci/vpbe_osd.c +++ b/drivers/media/platform/davinci/vpbe_osd.c @@ -119,7 +119,7 @@ static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, #define is_rgb_pixfmt(pixfmt) \ (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888)) #define is_yc_pixfmt(pixfmt) \ - (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \ + (((pixfmt) == PIXFMT_YCBCRI) || ((pixfmt) == PIXFMT_YCRCBI) || \ ((pixfmt) == PIXFMT_NV12)) #define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X #define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5) @@ -360,8 +360,8 @@ static void _osd_enable_color_key(struct osd_state *sd, osd_write(sd, colorkey & OSD_TRANSPVALL_RGBL, OSD_TRANSPVALL); break; - case PIXFMT_YCbCrI: - case PIXFMT_YCrCbI: + case PIXFMT_YCBCRI: + case PIXFMT_YCRCBI: if (sd->vpbe_type == VPBE_VERSION_3) osd_modify(sd, OSD_TRANSPVALU_Y, colorkey, OSD_TRANSPVALU); @@ -813,8 +813,8 @@ static int try_layer_config(struct osd_state *sd, enum osd_layer layer, if (osd->vpbe_type == VPBE_VERSION_1) bad_config = !is_vid_win(layer); break; - case PIXFMT_YCbCrI: - case PIXFMT_YCrCbI: + case PIXFMT_YCBCRI: + case PIXFMT_YCRCBI: bad_config = !is_vid_win(layer); break; case PIXFMT_RGB888: @@ -950,9 +950,9 @@ static void _osd_set_cbcr_order(struct osd_state *sd, * The caller must ensure that all windows using YC pixfmt use the same * Cb/Cr order. */ - if (pixfmt == PIXFMT_YCbCrI) + if (pixfmt == PIXFMT_YCBCRI) osd_clear(sd, OSD_MODE_CS, OSD_MODE); - else if (pixfmt == PIXFMT_YCrCbI) + else if (pixfmt == PIXFMT_YCRCBI) osd_set(sd, OSD_MODE_CS, OSD_MODE); } @@ -981,8 +981,8 @@ static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, winmd |= (2 << OSD_OSDWIN0MD_BMP0MD_SHIFT); _osd_enable_rgb888_pixblend(sd, OSDWIN_OSD0); break; - case PIXFMT_YCbCrI: - case PIXFMT_YCrCbI: + case PIXFMT_YCBCRI: + case PIXFMT_YCRCBI: winmd |= (3 << OSD_OSDWIN0MD_BMP0MD_SHIFT); break; default: @@ -1128,8 +1128,8 @@ static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, _osd_enable_rgb888_pixblend(sd, OSDWIN_OSD1); break; - case PIXFMT_YCbCrI: - case PIXFMT_YCrCbI: + case PIXFMT_YCBCRI: + case PIXFMT_YCRCBI: winmd |= (3 << OSD_OSDWIN1MD_BMP1MD_SHIFT); break; @@ -1508,7 +1508,7 @@ static int osd_initialize(struct osd_state *osd) _osd_init(osd); /* set default Cb/Cr order */ - osd->yc_pixfmt = PIXFMT_YCbCrI; + osd->yc_pixfmt = PIXFMT_YCBCRI; if (osd->vpbe_type == VPBE_VERSION_3) { /* diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/davinci/vpif.c index ea82a8bd280..cd08e524838 100644 --- a/drivers/media/platform/davinci/vpif.c +++ b/drivers/media/platform/davinci/vpif.c @@ -17,30 +17,26 @@ * GNU General Public License for more details. */ +#include <linux/err.h> #include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/kernel.h> -#include <linux/io.h> -#include <linux/err.h> #include <linux/pm_runtime.h> +#include <linux/spinlock.h> #include <linux/v4l2-dv-timings.h> -#include <mach/hardware.h> - #include "vpif.h" MODULE_DESCRIPTION("TI DaVinci Video Port Interface driver"); MODULE_LICENSE("GPL"); -#define VPIF_CH0_MAX_MODES (22) -#define VPIF_CH1_MAX_MODES (02) -#define VPIF_CH2_MAX_MODES (15) -#define VPIF_CH3_MAX_MODES (02) +#define VPIF_CH0_MAX_MODES 22 +#define VPIF_CH1_MAX_MODES 2 +#define VPIF_CH2_MAX_MODES 15 +#define VPIF_CH3_MAX_MODES 2 -static resource_size_t res_len; -static struct resource *res; spinlock_t vpif_lock; void __iomem *vpif_base; @@ -423,23 +419,12 @@ EXPORT_SYMBOL(vpif_channel_getfid); static int vpif_probe(struct platform_device *pdev) { - int status = 0; + static struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; - - res_len = resource_size(res); - - res = request_mem_region(res->start, res_len, res->name); - if (!res) - return -EBUSY; - - vpif_base = ioremap(res->start, res_len); - if (!vpif_base) { - status = -EBUSY; - goto fail; - } + vpif_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(vpif_base)) + return PTR_ERR(vpif_base); pm_runtime_enable(&pdev->dev); pm_runtime_get(&pdev->dev); @@ -447,17 +432,11 @@ static int vpif_probe(struct platform_device *pdev) spin_lock_init(&vpif_lock); dev_info(&pdev->dev, "vpif probe success\n"); return 0; - -fail: - release_mem_region(res->start, res_len); - return status; } static int vpif_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - iounmap(vpif_base); - release_mem_region(res->start, res_len); return 0; } diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 5f98df1fc8a..5514175bbd0 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -18,28 +18,16 @@ * TODO : add support for VBI & HBI data service * add static buffer allocation */ -#include <linux/kernel.h> -#include <linux/init.h> + #include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/mm.h> #include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/string.h> -#include <linux/videodev2.h> -#include <linux/wait.h> -#include <linux/time.h> -#include <linux/i2c.h> #include <linux/platform_device.h> -#include <linux/io.h> #include <linux/slab.h> -#include <media/v4l2-device.h> + #include <media/v4l2-ioctl.h> -#include <media/v4l2-chip-ident.h> -#include "vpif_capture.h" #include "vpif.h" +#include "vpif_capture.h" MODULE_DESCRIPTION("TI DaVinci VPIF Capture driver"); MODULE_LICENSE("GPL"); @@ -1874,66 +1862,6 @@ static int vpif_g_dv_timings(struct file *file, void *priv, } /* - * vpif_g_chip_ident() - Identify the chip - * @file: file ptr - * @priv: file handle - * @chip: chip identity - * - * Returns zero or -EINVAL if read operations fails. - */ -static int vpif_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && - chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { - vpif_dbg(2, debug, "match_type is invalid.\n"); - return -EINVAL; - } - - return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, - g_chip_ident, chip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * vpif_dbg_g_register() - Read register - * @file: file ptr - * @priv: file handle - * @reg: register to be read - * - * Debugging only - * Returns zero or -EINVAL if read operations fails. - */ -static int vpif_dbg_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_subdev_call(ch->sd, core, g_register, reg); -} - -/* - * vpif_dbg_s_register() - Write to register - * @file: file ptr - * @priv: file handle - * @reg: register to be modified - * - * Debugging only - * Returns zero or -EINVAL if write operations fails. - */ -static int vpif_dbg_s_register(struct file *file, void *priv, - const struct v4l2_dbg_register *reg) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_subdev_call(ch->sd, core, s_register, reg); -} -#endif - -/* * vpif_log_status() - Status information * @file: file ptr * @priv: file handle @@ -1974,11 +1902,6 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_query_dv_timings = vpif_query_dv_timings, .vidioc_s_dv_timings = vpif_s_dv_timings, .vidioc_g_dv_timings = vpif_g_dv_timings, - .vidioc_g_chip_ident = vpif_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vpif_dbg_g_register, - .vidioc_s_register = vpif_dbg_s_register, -#endif .vidioc_log_status = vpif_log_status, }; @@ -2092,16 +2015,13 @@ static __init int vpif_probe(struct platform_device *pdev) } while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { - for (i = res->start; i <= res->end; i++) { - if (request_irq(i, vpif_channel_isr, IRQF_SHARED, - "VPIF_Capture", (void *) - (&vpif_obj.dev[res_idx]->channel_id))) { - err = -EBUSY; - for (j = 0; j < i; j++) - free_irq(j, (void *) - (&vpif_obj.dev[res_idx]->channel_id)); - goto vpif_int_err; - } + err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, + IRQF_SHARED, "VPIF_Capture", + (void *)(&vpif_obj.dev[res_idx]-> + channel_id)); + if (err) { + err = -EINVAL; + goto vpif_unregister; } res_idx++; } @@ -2117,7 +2037,7 @@ static __init int vpif_probe(struct platform_device *pdev) video_device_release(ch->video_dev); } err = -ENOMEM; - goto vpif_int_err; + goto vpif_unregister; } /* Initialize field of video device */ @@ -2170,6 +2090,7 @@ static __init int vpif_probe(struct platform_device *pdev) if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); + err = -ENODEV; goto probe_subdev_out; } v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n", @@ -2217,13 +2138,9 @@ vpif_sd_error: /* Note: does nothing if ch->video_dev == NULL */ video_device_release(ch->video_dev); } -vpif_int_err: +vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); - for (i = 0; i < res_idx; i++) { - res = platform_get_resource(pdev, IORESOURCE_IRQ, i); - for (j = res->start; j <= res->end; j++) - free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id)); - } + return err; } @@ -2235,17 +2152,19 @@ vpif_int_err: */ static int vpif_remove(struct platform_device *device) { - int i; struct channel_obj *ch; + int i; v4l2_device_unregister(&vpif_obj.v4l2_dev); + kfree(vpif_obj.sd); /* un-register device */ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; /* Unregister video device */ video_unregister_device(ch->video_dev); + kfree(vpif_obj.dev[i]); } return 0; } @@ -2336,47 +2255,4 @@ static __refdata struct platform_driver vpif_driver = { .remove = vpif_remove, }; -/** - * vpif_init: initialize the vpif driver - * - * This function registers device and driver to the kernel, requests irq - * handler and allocates memory - * for channel objects - */ -static __init int vpif_init(void) -{ - return platform_driver_register(&vpif_driver); -} - -/** - * vpif_cleanup : This function clean up the vpif capture resources - * - * This will un-registers device and driver to the kernel, frees - * requested irq handler and de-allocates memory allocated for channel - * objects. - */ -static void vpif_cleanup(void) -{ - struct platform_device *pdev; - struct resource *res; - int irq_num; - int i = 0; - - pdev = container_of(vpif_dev, struct platform_device, dev); - while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { - for (irq_num = res->start; irq_num <= res->end; irq_num++) - free_irq(irq_num, - (void *)(&vpif_obj.dev[i]->channel_id)); - i++; - } - - platform_driver_unregister(&vpif_driver); - - kfree(vpif_obj.sd); - for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) - kfree(vpif_obj.dev[i]); -} - -/* Function for module initialization and cleanup */ -module_init(vpif_init); -module_exit(vpif_cleanup); +module_platform_driver(vpif_driver); diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h index 3d3c1e5cd5d..0ebb3126036 100644 --- a/drivers/media/platform/davinci/vpif_capture.h +++ b/drivers/media/platform/davinci/vpif_capture.h @@ -22,11 +22,8 @@ #ifdef __KERNEL__ /* Header files */ -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-device.h> #include <media/videobuf2-dma-contig.h> -#include <media/davinci/vpif_types.h> +#include <media/v4l2-device.h> #include "vpif.h" diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 1b3fb5ca2ad..e6e57365025 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -14,33 +14,15 @@ * GNU General Public License for more details. */ -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/mm.h> #include <linux/interrupt.h> -#include <linux/workqueue.h> -#include <linux/string.h> -#include <linux/videodev2.h> -#include <linux/wait.h> -#include <linux/time.h> -#include <linux/i2c.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/io.h> #include <linux/slab.h> -#include <asm/irq.h> -#include <asm/page.h> - -#include <media/adv7343.h> -#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-chip-ident.h> -#include "vpif_display.h" #include "vpif.h" +#include "vpif_display.h" MODULE_DESCRIPTION("TI DaVinci VPIF Display driver"); MODULE_LICENSE("GPL"); @@ -1518,66 +1500,6 @@ static int vpif_g_dv_timings(struct file *file, void *priv, } /* - * vpif_g_chip_ident() - Identify the chip - * @file: file ptr - * @priv: file handle - * @chip: chip identity - * - * Returns zero or -EINVAL if read operations fails. - */ -static int vpif_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && - chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) { - vpif_dbg(2, debug, "match_type is invalid.\n"); - return -EINVAL; - } - - return v4l2_device_call_until_err(&vpif_obj.v4l2_dev, 0, core, - g_chip_ident, chip); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * vpif_dbg_g_register() - Read register - * @file: file ptr - * @priv: file handle - * @reg: register to be read - * - * Debugging only - * Returns zero or -EINVAL if read operations fails. - */ -static int vpif_dbg_g_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg){ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_subdev_call(ch->sd, core, g_register, reg); -} - -/* - * vpif_dbg_s_register() - Write to register - * @file: file ptr - * @priv: file handle - * @reg: register to be modified - * - * Debugging only - * Returns zero or -EINVAL if write operations fails. - */ -static int vpif_dbg_s_register(struct file *file, void *priv, - const struct v4l2_dbg_register *reg) -{ - struct vpif_fh *fh = priv; - struct channel_obj *ch = fh->channel; - - return v4l2_subdev_call(ch->sd, core, s_register, reg); -} -#endif - -/* * vpif_log_status() - Status information * @file: file ptr * @priv: file handle @@ -1616,11 +1538,6 @@ static const struct v4l2_ioctl_ops vpif_ioctl_ops = { .vidioc_enum_dv_timings = vpif_enum_dv_timings, .vidioc_s_dv_timings = vpif_s_dv_timings, .vidioc_g_dv_timings = vpif_g_dv_timings, - .vidioc_g_chip_ident = vpif_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vpif_dbg_g_register, - .vidioc_s_register = vpif_dbg_s_register, -#endif .vidioc_log_status = vpif_log_status, }; @@ -1734,16 +1651,14 @@ static __init int vpif_probe(struct platform_device *pdev) } while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) { - for (i = res->start; i <= res->end; i++) { - if (request_irq(i, vpif_channel_isr, IRQF_SHARED, - "VPIF_Display", (void *) - (&vpif_obj.dev[res_idx]->channel_id))) { - err = -EBUSY; - for (j = 0; j < i; j++) - free_irq(j, (void *) - (&vpif_obj.dev[res_idx]->channel_id)); - goto vpif_int_err; - } + err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr, + IRQF_SHARED, "VPIF_Display", + (void *)(&vpif_obj.dev[res_idx]-> + channel_id)); + if (err) { + err = -EINVAL; + vpif_err("VPIF IRQ request failed\n"); + goto vpif_unregister; } res_idx++; } @@ -1760,7 +1675,7 @@ static __init int vpif_probe(struct platform_device *pdev) video_device_release(ch->video_dev); } err = -ENOMEM; - goto vpif_int_err; + goto vpif_unregister; } /* Initialize field of video device */ @@ -1813,6 +1728,7 @@ static __init int vpif_probe(struct platform_device *pdev) NULL); if (!vpif_obj.sd[i]) { vpif_err("Error registering v4l2 subdevice\n"); + err = -ENODEV; goto probe_subdev_out; } @@ -1893,14 +1809,8 @@ vpif_sd_error: /* Note: does nothing if ch->video_dev == NULL */ video_device_release(ch->video_dev); } -vpif_int_err: +vpif_unregister: v4l2_device_unregister(&vpif_obj.v4l2_dev); - vpif_err("VPIF IRQ request failed\n"); - for (i = 0; i < res_idx; i++) { - res = platform_get_resource(pdev, IORESOURCE_IRQ, i); - for (j = res->start; j <= res->end; j++) - free_irq(j, (void *)(&vpif_obj.dev[i]->channel_id)); - } return err; } @@ -1915,6 +1825,7 @@ static int vpif_remove(struct platform_device *device) v4l2_device_unregister(&vpif_obj.v4l2_dev); + kfree(vpif_obj.sd); /* un-register device */ for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ @@ -1923,6 +1834,7 @@ static int vpif_remove(struct platform_device *device) video_unregister_device(ch->video_dev); ch->video_dev = NULL; + kfree(vpif_obj.dev[i]); } return 0; @@ -2008,37 +1920,4 @@ static __refdata struct platform_driver vpif_driver = { .remove = vpif_remove, }; -static __init int vpif_init(void) -{ - return platform_driver_register(&vpif_driver); -} - -/* - * vpif_cleanup: This function un-registers device and driver to the kernel, - * frees requested irq handler and de-allocates memory allocated for channel - * objects. - */ -static void vpif_cleanup(void) -{ - struct platform_device *pdev; - struct resource *res; - int irq_num; - int i = 0; - - pdev = container_of(vpif_dev, struct platform_device, dev); - - while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, i))) { - for (irq_num = res->start; irq_num <= res->end; irq_num++) - free_irq(irq_num, - (void *)(&vpif_obj.dev[i]->channel_id)); - i++; - } - - platform_driver_unregister(&vpif_driver); - kfree(vpif_obj.sd); - for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) - kfree(vpif_obj.dev[i]); -} - -module_init(vpif_init); -module_exit(vpif_cleanup); +module_platform_driver(vpif_driver); diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h index a5a18f74395..5d87fc86e58 100644 --- a/drivers/media/platform/davinci/vpif_display.h +++ b/drivers/media/platform/davinci/vpif_display.h @@ -17,11 +17,8 @@ #define DAVINCIHD_DISPLAY_H /* Header files */ -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-device.h> #include <media/videobuf2-dma-contig.h> -#include <media/davinci/vpif_types.h> +#include <media/v4l2-device.h> #include "vpif.h" diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index 33b5ffc8d66..559fab2a2d6 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -988,7 +988,7 @@ static void *gsc_get_drv_data(struct platform_device *pdev) if (pdev->dev.of_node) { const struct of_device_id *match; - match = of_match_node(of_match_ptr(exynos_gsc_match), + match = of_match_node(exynos_gsc_match, pdev->dev.of_node); if (match) driver_data = (struct gsc_driverdata *)match->data; diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/exynos4-is/Kconfig index 436a62a995e..53ad0f08017 100644 --- a/drivers/media/platform/exynos4-is/Kconfig +++ b/drivers/media/platform/exynos4-is/Kconfig @@ -9,12 +9,16 @@ config VIDEO_SAMSUNG_EXYNOS4_IS if VIDEO_SAMSUNG_EXYNOS4_IS +config VIDEO_EXYNOS4_IS_COMMON + tristate + config VIDEO_S5P_FIMC tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" depends on I2C select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV select MFD_SYSCON if OF + select VIDEO_EXYNOS4_IS_COMMON help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host interface and video postprocessor (FIMC) devices. @@ -39,6 +43,7 @@ config VIDEO_EXYNOS_FIMC_LITE tristate "EXYNOS FIMC-LITE camera interface driver" depends on I2C select VIDEOBUF2_DMA_CONTIG + select VIDEO_EXYNOS4_IS_COMMON help This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera host interface. diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/exynos4-is/Makefile index f25f4637739..c2ff29ba685 100644 --- a/drivers/media/platform/exynos4-is/Makefile +++ b/drivers/media/platform/exynos4-is/Makefile @@ -1,10 +1,13 @@ s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o media-dev.o exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o +s5p-csis-objs := mipi-csis.o +exynos4-is-common-objs := common.o + exynos-fimc-is-objs := fimc-is.o fimc-isp.o fimc-is-sensor.o fimc-is-regs.o exynos-fimc-is-objs += fimc-is-param.o fimc-is-errno.o fimc-is-i2c.o -s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o obj-$(CONFIG_VIDEO_EXYNOS4_FIMC_IS) += exynos-fimc-is.o obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o +obj-$(CONFIG_VIDEO_EXYNOS4_IS_COMMON) += exynos4-is-common.o diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/exynos4-is/common.c new file mode 100644 index 00000000000..0ec210b4da1 --- /dev/null +++ b/drivers/media/platform/exynos4-is/common.c @@ -0,0 +1,53 @@ +/* + * Samsung S5P/EXYNOS4 SoC Camera Subsystem driver + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> + * + * 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 <linux/module.h> +#include <media/s5p_fimc.h> +#include "common.h" + +/* Called with the media graph mutex held or entity->stream_count > 0. */ +struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity) +{ + struct media_pad *pad = &entity->pads[0]; + struct v4l2_subdev *sd; + + while (pad->flags & MEDIA_PAD_FL_SINK) { + /* source pad */ + pad = media_entity_remote_pad(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR || + sd->grp_id == GRP_ID_SENSOR) + return sd; + /* sink pad */ + pad = &sd->entity.pads[0]; + } + return NULL; +} +EXPORT_SYMBOL(fimc_find_remote_sensor); + +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps) +{ + strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); + strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(dev)); + cap->device_caps = caps; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; +} +EXPORT_SYMBOL(__fimc_vidioc_querycap); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/exynos4-is/common.h new file mode 100644 index 00000000000..75b9c71d941 --- /dev/null +++ b/drivers/media/platform/exynos4-is/common.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * 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 <linux/device.h> +#include <linux/videodev2.h> +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity); +void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, + unsigned int caps); diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/exynos4-is/fimc-capture.c index 528f4136936..fb27ff7e1e0 100644 --- a/drivers/media/platform/exynos4-is/fimc-capture.c +++ b/drivers/media/platform/exynos4-is/fimc-capture.c @@ -27,9 +27,10 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include "media-dev.h" +#include "common.h" #include "fimc-core.h" #include "fimc-reg.h" +#include "media-dev.h" static int fimc_capture_hw_init(struct fimc_dev *fimc) { @@ -119,8 +120,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) spin_unlock_irqrestore(&fimc->slock, flags); if (streaming) - return fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 0); + return fimc_pipeline_call(&cap->ve, set_stream, 0); else return 0; } @@ -178,8 +178,9 @@ static int fimc_capture_config_update(struct fimc_ctx *ctx) void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) { - struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_pipeline *p = to_fimc_pipeline(cap->ve.pipe); + struct v4l2_subdev *csis = p->subdevs[IDX_CSIS]; struct fimc_frame *f = &cap->ctx->d_frame; struct fimc_vid_buffer *v_buf; struct timeval *tv; @@ -287,8 +288,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc_activate_capture(ctx); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - return fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + return fimc_pipeline_call(&vid_cap->ve, set_stream, 1); } return 0; @@ -312,7 +312,7 @@ int fimc_capture_suspend(struct fimc_dev *fimc) int ret = fimc_stop_capture(fimc, suspend); if (ret) return ret; - return fimc_pipeline_call(fimc, close, &fimc->pipeline); + return fimc_pipeline_call(&fimc->vid_cap.ve, close); } static void buffer_queue(struct vb2_buffer *vb); @@ -320,6 +320,7 @@ static void buffer_queue(struct vb2_buffer *vb); int fimc_capture_resume(struct fimc_dev *fimc) { struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct exynos_video_entity *ve = &vid_cap->ve; struct fimc_vid_buffer *buf; int i; @@ -328,8 +329,7 @@ int fimc_capture_resume(struct fimc_dev *fimc) INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); vid_cap->buf_index = 0; - fimc_pipeline_call(fimc, open, &fimc->pipeline, - &vid_cap->vfd.entity, false); + fimc_pipeline_call(ve, open, &ve->vdev.entity, false); fimc_capture_hw_init(fimc); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); @@ -397,7 +397,7 @@ static int buffer_prepare(struct vb2_buffer *vb) unsigned long size = ctx->d_frame.payload[i]; if (vb2_plane_size(vb, i) < size) { - v4l2_err(&ctx->fimc_dev->vid_cap.vfd, + v4l2_err(&ctx->fimc_dev->vid_cap.ve.vdev, "User buffer too small (%ld < %ld)\n", vb2_plane_size(vb, i), size); return -EINVAL; @@ -415,6 +415,7 @@ static void buffer_queue(struct vb2_buffer *vb) struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); struct fimc_dev *fimc = ctx->fimc_dev; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + struct exynos_video_entity *ve = &vid_cap->ve; unsigned long flags; int min_bufs; @@ -452,9 +453,9 @@ static void buffer_queue(struct vb2_buffer *vb) if (test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) return; - ret = fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 1); + ret = fimc_pipeline_call(ve, set_stream, 1); if (ret < 0) - v4l2_err(&vid_cap->vfd, "stream on failed: %d\n", ret); + v4l2_err(&ve->vdev, "stream on failed: %d\n", ret); return; } spin_unlock_irqrestore(&fimc->slock, flags); @@ -470,44 +471,17 @@ static struct vb2_ops fimc_capture_qops = { .stop_streaming = stop_streaming, }; -/** - * fimc_capture_ctrls_create - initialize the control handler - * Initialize the capture video node control handler and fill it - * with the FIMC controls. Inherit any sensor's controls if the - * 'user_subdev_api' flag is false (default behaviour). - * This function need to be called with the graph mutex held. - */ -int fimc_capture_ctrls_create(struct fimc_dev *fimc) -{ - struct fimc_vid_cap *vid_cap = &fimc->vid_cap; - struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; - int ret; - - if (WARN_ON(vid_cap->ctx == NULL)) - return -ENXIO; - if (vid_cap->ctx->ctrls.ready) - return 0; - - ret = fimc_ctrls_create(vid_cap->ctx); - - if (ret || vid_cap->user_subdev_api || !sensor || - !vid_cap->ctx->ctrls.ready) - return ret; - - return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, - sensor->ctrl_handler, NULL); -} - static int fimc_capture_set_default_format(struct fimc_dev *fimc); static int fimc_capture_open(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct exynos_video_entity *ve = &vc->ve; int ret = -EBUSY; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); - fimc_md_graph_lock(fimc); mutex_lock(&fimc->lock); if (fimc_m2m_active(fimc)) @@ -520,31 +494,42 @@ static int fimc_capture_open(struct file *file) ret = v4l2_fh_open(file); if (ret) { - pm_runtime_put(&fimc->pdev->dev); + pm_runtime_put_sync(&fimc->pdev->dev); goto unlock; } if (v4l2_fh_is_singular_file(file)) { - ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, - &fimc->vid_cap.vfd.entity, true); + fimc_md_graph_lock(ve); - if (!ret && !fimc->vid_cap.user_subdev_api) - ret = fimc_capture_set_default_format(fimc); + ret = fimc_pipeline_call(ve, open, &ve->vdev.entity, true); - if (!ret) - ret = fimc_capture_ctrls_create(fimc); + if (ret == 0 && vc->user_subdev_api && vc->inh_sensor_ctrls) { + /* + * Recreate controls of the the video node to drop + * any controls inherited from the sensor subdev. + */ + fimc_ctrls_delete(vc->ctx); + + ret = fimc_ctrls_create(vc->ctx); + if (ret == 0) + vc->inh_sensor_ctrls = false; + } + if (ret == 0) + ve->vdev.entity.use_count++; + + fimc_md_graph_unlock(ve); + + if (ret == 0) + ret = fimc_capture_set_default_format(fimc); if (ret < 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); pm_runtime_put_sync(&fimc->pdev->dev); v4l2_fh_release(file); - } else { - fimc->vid_cap.refcnt++; } } unlock: mutex_unlock(&fimc->lock); - fimc_md_graph_unlock(fimc); return ret; } @@ -552,30 +537,31 @@ static int fimc_capture_release(struct file *file) { struct fimc_dev *fimc = video_drvdata(file); struct fimc_vid_cap *vc = &fimc->vid_cap; + bool close = v4l2_fh_is_singular_file(file); int ret; dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state); mutex_lock(&fimc->lock); - if (v4l2_fh_is_singular_file(file)) { - if (vc->streaming) { - media_entity_pipeline_stop(&vc->vfd.entity); - vc->streaming = false; - } - clear_bit(ST_CAPT_BUSY, &fimc->state); - fimc_stop_capture(fimc, false); - fimc_pipeline_call(fimc, close, &fimc->pipeline); - clear_bit(ST_CAPT_SUSPENDED, &fimc->state); - fimc->vid_cap.refcnt--; + if (close && vc->streaming) { + media_entity_pipeline_stop(&vc->ve.vdev.entity); + vc->streaming = false; } - pm_runtime_put(&fimc->pdev->dev); + ret = vb2_fop_release(file); - if (v4l2_fh_is_singular_file(file)) - fimc_ctrls_delete(fimc->vid_cap.ctx); + if (close) { + clear_bit(ST_CAPT_BUSY, &fimc->state); + fimc_pipeline_call(&vc->ve, close); + clear_bit(ST_CAPT_SUSPENDED, &fimc->state); - ret = vb2_fop_release(file); + fimc_md_graph_lock(&vc->ve); + vc->ve.vdev.entity.use_count--; + fimc_md_graph_unlock(&vc->ve); + } + + pm_runtime_put_sync(&fimc->pdev->dev); mutex_unlock(&fimc->lock); return ret; @@ -773,7 +759,7 @@ static struct media_entity *fimc_pipeline_get_head(struct media_entity *me) struct media_pad *pad = &me->pads[0]; while (!(pad->flags & MEDIA_PAD_FL_SOURCE)) { - pad = media_entity_remote_source(pad); + pad = media_entity_remote_pad(pad); if (!pad) break; me = pad->entity; @@ -797,7 +783,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, bool set) { struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + struct fimc_pipeline *p = to_fimc_pipeline(fimc->vid_cap.ve.pipe); + struct v4l2_subdev *sd = p->subdevs[IDX_SENSOR]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct media_entity *me; @@ -845,7 +832,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, return ret; } - pad = media_entity_remote_source(&me->pads[sfmt.pad]); + pad = media_entity_remote_pad(&me->pads[sfmt.pad]); if (!pad) return -EINVAL; me = pad->entity; @@ -929,57 +916,101 @@ static int fimc_cap_g_fmt_mplane(struct file *file, void *fh, return 0; } -static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) +/* + * Try or set format on the fimc.X.capture video node and additionally + * on the whole pipeline if @try is false. + * Locking: the caller must _not_ hold the graph mutex. + */ +static int __video_try_or_set_format(struct fimc_dev *fimc, + struct v4l2_format *f, bool try, + struct fimc_fmt **inp_fmt, + struct fimc_fmt **out_fmt) { struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct v4l2_mbus_framefmt mf; - struct fimc_fmt *ffmt = NULL; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct exynos_video_entity *ve = &vc->ve; + struct fimc_ctx *ctx = vc->ctx; + unsigned int width = 0, height = 0; int ret = 0; - fimc_md_graph_lock(fimc); - mutex_lock(&fimc->lock); - + /* Pre-configure format at the camera input interface, for JPEG only */ if (fimc_jpeg_fourcc(pix->pixelformat)) { fimc_capture_try_format(ctx, &pix->width, &pix->height, NULL, &pix->pixelformat, FIMC_SD_PAD_SINK_CAM); - ctx->s_frame.f_width = pix->width; - ctx->s_frame.f_height = pix->height; + if (try) { + width = pix->width; + height = pix->height; + } else { + ctx->s_frame.f_width = pix->width; + ctx->s_frame.f_height = pix->height; + } } - ffmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, - NULL, &pix->pixelformat, - FIMC_SD_PAD_SOURCE); - if (!ffmt) { - ret = -EINVAL; - goto unlock; + + /* Try the format at the scaler and the DMA output */ + *out_fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, + NULL, &pix->pixelformat, + FIMC_SD_PAD_SOURCE); + if (*out_fmt == NULL) + return -EINVAL; + + /* Restore image width/height for JPEG (no resizing supported). */ + if (try && fimc_jpeg_fourcc(pix->pixelformat)) { + pix->width = width; + pix->height = height; } - if (!fimc->vid_cap.user_subdev_api) { - mf.width = pix->width; - mf.height = pix->height; - mf.code = ffmt->mbus_code; - fimc_pipeline_try_format(ctx, &mf, &ffmt, false); - pix->width = mf.width; - pix->height = mf.height; - if (ffmt) - pix->pixelformat = ffmt->fourcc; + /* Try to match format at the host and the sensor */ + if (!vc->user_subdev_api) { + struct v4l2_mbus_framefmt mbus_fmt; + struct v4l2_mbus_framefmt *mf; + + mf = try ? &mbus_fmt : &fimc->vid_cap.ci_fmt; + + mf->code = (*out_fmt)->mbus_code; + mf->width = pix->width; + mf->height = pix->height; + + fimc_md_graph_lock(ve); + ret = fimc_pipeline_try_format(ctx, mf, inp_fmt, try); + fimc_md_graph_unlock(ve); + + if (ret < 0) + return ret; + + pix->width = mf->width; + pix->height = mf->height; } - fimc_adjust_mplane_format(ffmt, pix->width, pix->height, pix); + fimc_adjust_mplane_format(*out_fmt, pix->width, pix->height, pix); - if (ffmt->flags & FMT_FLAGS_COMPRESSED) - fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], - pix->plane_fmt, ffmt->memplanes, true); -unlock: - mutex_unlock(&fimc->lock); - fimc_md_graph_unlock(fimc); + if ((*out_fmt)->flags & FMT_FLAGS_COMPRESSED) { + struct v4l2_subdev *sensor; + + fimc_md_graph_lock(ve); + + sensor = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR); + if (sensor) + fimc_get_sensor_frame_desc(sensor, pix->plane_fmt, + (*out_fmt)->memplanes, try); + else + ret = -EPIPE; + + fimc_md_graph_unlock(ve); + } return ret; } +static int fimc_cap_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_fmt *out_fmt = NULL, *inp_fmt = NULL; + + return __video_try_or_set_format(fimc, f, true, &inp_fmt, &out_fmt); +} + static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, enum fimc_color_fmt color) { @@ -997,57 +1028,23 @@ static void fimc_capture_mark_jpeg_xfer(struct fimc_ctx *ctx, static int __fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) { - struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct fimc_ctx *ctx = vc->ctx; struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct v4l2_mbus_framefmt *mf = &fimc->vid_cap.ci_fmt; struct fimc_frame *ff = &ctx->d_frame; - struct fimc_fmt *s_fmt = NULL; + struct fimc_fmt *inp_fmt = NULL; int ret, i; if (vb2_is_busy(&fimc->vid_cap.vbq)) return -EBUSY; - /* Pre-configure format at camera interface input, for JPEG only */ - if (fimc_jpeg_fourcc(pix->pixelformat)) { - fimc_capture_try_format(ctx, &pix->width, &pix->height, - NULL, &pix->pixelformat, - FIMC_SD_PAD_SINK_CAM); - ctx->s_frame.f_width = pix->width; - ctx->s_frame.f_height = pix->height; - } - /* Try the format at the scaler and the DMA output */ - ff->fmt = fimc_capture_try_format(ctx, &pix->width, &pix->height, - NULL, &pix->pixelformat, - FIMC_SD_PAD_SOURCE); - if (!ff->fmt) - return -EINVAL; + ret = __video_try_or_set_format(fimc, f, false, &inp_fmt, &ff->fmt); + if (ret < 0) + return ret; /* Update RGB Alpha control state and value range */ fimc_alpha_ctrl_update(ctx); - /* Try to match format at the host and the sensor */ - if (!fimc->vid_cap.user_subdev_api) { - mf->code = ff->fmt->mbus_code; - mf->width = pix->width; - mf->height = pix->height; - ret = fimc_pipeline_try_format(ctx, mf, &s_fmt, true); - if (ret) - return ret; - - pix->width = mf->width; - pix->height = mf->height; - } - - fimc_adjust_mplane_format(ff->fmt, pix->width, pix->height, pix); - - if (ff->fmt->flags & FMT_FLAGS_COMPRESSED) { - ret = fimc_get_sensor_frame_desc(fimc->pipeline.subdevs[IDX_SENSOR], - pix->plane_fmt, ff->fmt->memplanes, - true); - if (ret < 0) - return ret; - } - for (i = 0; i < ff->fmt->memplanes; i++) { ff->bytesperline[i] = pix->plane_fmt[i].bytesperline; ff->payload[i] = pix->plane_fmt[i].sizeimage; @@ -1061,8 +1058,8 @@ static int __fimc_capture_set_format(struct fimc_dev *fimc, fimc_capture_mark_jpeg_xfer(ctx, ff->fmt->color); /* Reset cropping and set format at the camera interface input */ - if (!fimc->vid_cap.user_subdev_api) { - ctx->s_frame.fmt = s_fmt; + if (!vc->user_subdev_api) { + ctx->s_frame.fmt = inp_fmt; set_frame_bounds(&ctx->s_frame, pix->width, pix->height); set_frame_crop(&ctx->s_frame, 0, 0, pix->width, pix->height); } @@ -1074,37 +1071,28 @@ static int fimc_cap_s_fmt_mplane(struct file *file, void *priv, struct v4l2_format *f) { struct fimc_dev *fimc = video_drvdata(file); - int ret; - fimc_md_graph_lock(fimc); - mutex_lock(&fimc->lock); - /* - * The graph is walked within __fimc_capture_set_format() to set - * the format at subdevs thus the graph mutex needs to be held at - * this point and acquired before the video mutex, to avoid AB-BA - * deadlock when fimc_md_link_notify() is called by other thread. - * Ideally the graph walking and setting format at the whole pipeline - * should be removed from this driver and handled in userspace only. - */ - ret = __fimc_capture_set_format(fimc, f); - - mutex_unlock(&fimc->lock); - fimc_md_graph_unlock(fimc); - return ret; + return __fimc_capture_set_format(fimc, f); } static int fimc_cap_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + struct exynos_video_entity *ve = &fimc->vid_cap.ve; + struct v4l2_subdev *sd; if (i->index != 0) return -EINVAL; i->type = V4L2_INPUT_TYPE_CAMERA; + fimc_md_graph_lock(ve); + sd = __fimc_md_get_subdev(ve->pipe, IDX_SENSOR); + fimc_md_graph_unlock(ve); + if (sd) strlcpy(i->name, sd->name, sizeof(i->name)); + return 0; } @@ -1130,6 +1118,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) struct v4l2_subdev_format sink_fmt, src_fmt; struct fimc_vid_cap *vc = &fimc->vid_cap; struct v4l2_subdev *sd = &vc->subdev; + struct fimc_pipeline *p = to_fimc_pipeline(vc->ve.pipe); struct media_pad *sink_pad, *src_pad; int i, ret; @@ -1146,7 +1135,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (p->flags & MEDIA_PAD_FL_SINK) { sink_pad = p; - src_pad = media_entity_remote_source(sink_pad); + src_pad = media_entity_remote_pad(sink_pad); if (src_pad) break; } @@ -1183,7 +1172,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) src_fmt.format.code != sink_fmt.format.code) return -EPIPE; - if (sd == fimc->pipeline.subdevs[IDX_SENSOR] && + if (sd == p->subdevs[IDX_SENSOR] && fimc_user_defined_mbus_fmt(src_fmt.format.code)) { struct v4l2_plane_pix_format plane_fmt[FIMC_MAX_PLANES]; struct fimc_frame *frame = &vc->ctx->d_frame; @@ -1207,9 +1196,8 @@ static int fimc_cap_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); - struct fimc_pipeline *p = &fimc->pipeline; struct fimc_vid_cap *vc = &fimc->vid_cap; - struct media_entity *entity = &vc->vfd.entity; + struct media_entity *entity = &vc->ve.vdev.entity; struct fimc_source_info *si = NULL; struct v4l2_subdev *sd; int ret; @@ -1217,11 +1205,11 @@ static int fimc_cap_streamon(struct file *file, void *priv, if (fimc_capture_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, &vc->ve.pipe->mp); if (ret < 0) return ret; - sd = p->subdevs[IDX_SENSOR]; + sd = __fimc_md_get_subdev(vc->ve.pipe, IDX_SENSOR); if (sd) si = v4l2_get_subdev_hostdata(sd); @@ -1259,14 +1247,15 @@ static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); + struct fimc_vid_cap *vc = &fimc->vid_cap; int ret; ret = vb2_ioctl_streamoff(file, priv, type); if (ret < 0) return ret; - media_entity_pipeline_stop(&fimc->vid_cap.vfd.entity); - fimc->vid_cap.streaming = false; + media_entity_pipeline_stop(&vc->ve.vdev.entity); + vc->streaming = false; return 0; } @@ -1405,6 +1394,8 @@ static int fimc_link_setup(struct media_entity *entity, { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct fimc_vid_cap *vc = &fimc->vid_cap; + struct v4l2_subdev *sensor; if (media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return -EINVAL; @@ -1416,15 +1407,26 @@ static int fimc_link_setup(struct media_entity *entity, local->entity->name, remote->entity->name, flags, fimc->vid_cap.input); - if (flags & MEDIA_LNK_FL_ENABLED) { - if (fimc->vid_cap.input != 0) - return -EBUSY; - fimc->vid_cap.input = sd->grp_id; + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + fimc->vid_cap.input = 0; return 0; } - fimc->vid_cap.input = 0; - return 0; + if (vc->input != 0) + return -EBUSY; + + vc->input = sd->grp_id; + + if (vc->user_subdev_api || vc->inh_sensor_ctrls) + return 0; + + /* Inherit V4L2 controls from the image sensor subdev. */ + sensor = fimc_find_remote_sensor(&vc->subdev.entity); + if (sensor == NULL) + return 0; + + return v4l2_ctrl_add_handler(&vc->ctx->ctrls.handler, + sensor->ctrl_handler, NULL); } static const struct media_entity_operations fimc_sd_media_ops = { @@ -1720,8 +1722,8 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, .fmt.pix_mp = { - .width = 640, - .height = 480, + .width = FIMC_DEFAULT_WIDTH, + .height = FIMC_DEFAULT_HEIGHT, .pixelformat = V4L2_PIX_FMT_YUYV, .field = V4L2_FIELD_NONE, .colorspace = V4L2_COLORSPACE_JPEG, @@ -1735,10 +1737,11 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) static int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev) { - struct video_device *vfd = &fimc->vid_cap.vfd; + struct video_device *vfd = &fimc->vid_cap.ve.vdev; struct vb2_queue *q = &fimc->vid_cap.vbq; struct fimc_ctx *ctx; struct fimc_vid_cap *vid_cap; + struct fimc_fmt *fmt; int ret = -ENOMEM; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); @@ -1784,22 +1787,34 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, ret = vb2_queue_init(q); if (ret) - goto err_ent; + goto err_free_ctx; + + /* Default format configuration */ + fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); + vid_cap->ci_fmt.width = FIMC_DEFAULT_WIDTH; + vid_cap->ci_fmt.height = FIMC_DEFAULT_HEIGHT; + vid_cap->ci_fmt.code = fmt->mbus_code; + + ctx->s_frame.width = FIMC_DEFAULT_WIDTH; + ctx->s_frame.height = FIMC_DEFAULT_HEIGHT; + ctx->s_frame.fmt = fmt; + + fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_WRITEBACK, 0); + vid_cap->wb_fmt = vid_cap->ci_fmt; + vid_cap->wb_fmt.code = fmt->mbus_code; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) - goto err_ent; - /* - * For proper order of acquiring/releasing the video - * and the graph mutex. - */ - v4l2_disable_ioctl_locking(vfd, VIDIOC_TRY_FMT); - v4l2_disable_ioctl_locking(vfd, VIDIOC_S_FMT); + goto err_free_ctx; + + ret = fimc_ctrls_create(ctx); + if (ret) + goto err_me_cleanup; ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret) - goto err_vd; + goto err_ctrl_free; v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", vfd->name, video_device_node_name(vfd)); @@ -1807,9 +1822,11 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, vfd->ctrl_handler = &ctx->ctrls.handler; return 0; -err_vd: +err_ctrl_free: + fimc_ctrls_delete(ctx); +err_me_cleanup: media_entity_cleanup(&vfd->entity); -err_ent: +err_free_ctx: kfree(ctx); return ret; } @@ -1826,12 +1843,12 @@ static int fimc_capture_subdev_registered(struct v4l2_subdev *sd) if (ret) return ret; - fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd); + fimc->vid_cap.ve.pipe = v4l2_get_subdev_hostdata(sd); ret = fimc_register_capture_device(fimc, sd->v4l2_dev); if (ret) { fimc_unregister_m2m_device(fimc); - fimc->pipeline_ops = NULL; + fimc->vid_cap.ve.pipe = NULL; } return ret; @@ -1840,19 +1857,26 @@ static int fimc_capture_subdev_registered(struct v4l2_subdev *sd) static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + struct video_device *vdev; if (fimc == NULL) return; + mutex_lock(&fimc->lock); + fimc_unregister_m2m_device(fimc); + vdev = &fimc->vid_cap.ve.vdev; - if (video_is_registered(&fimc->vid_cap.vfd)) { - video_unregister_device(&fimc->vid_cap.vfd); - media_entity_cleanup(&fimc->vid_cap.vfd.entity); - fimc->pipeline_ops = NULL; + if (video_is_registered(vdev)) { + video_unregister_device(vdev); + media_entity_cleanup(&vdev->entity); + fimc_ctrls_delete(fimc->vid_cap.ctx); + fimc->vid_cap.ve.pipe = NULL; } kfree(fimc->vid_cap.ctx); fimc->vid_cap.ctx = NULL; + + mutex_unlock(&fimc->lock); } static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = { diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c index 379a5e9d52a..6489c5160ee 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.c +++ b/drivers/media/platform/exynos4-is/fimc-core.c @@ -213,17 +213,6 @@ struct fimc_fmt *fimc_get_format(unsigned int index) return &fimc_formats[index]; } -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps) -{ - strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver)); - strlcpy(cap->card, dev->driver->name, sizeof(cap->card)); - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", dev_name(dev)); - cap->device_caps = caps; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; -} - int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation) { diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/exynos4-is/fimc-core.h index 539a3f71c16..3d376faec77 100644 --- a/drivers/media/platform/exynos4-is/fimc-core.h +++ b/drivers/media/platform/exynos4-is/fimc-core.h @@ -48,6 +48,8 @@ #define FIMC_DEF_MIN_SIZE 16 #define FIMC_DEF_HEIGHT_ALIGN 2 #define FIMC_DEF_HOR_OFFS_ALIGN 1 +#define FIMC_DEFAULT_WIDTH 640 +#define FIMC_DEFAULT_HEIGHT 480 /* indices to the clocks array */ enum { @@ -283,8 +285,8 @@ struct fimc_m2m_device { /** * struct fimc_vid_cap - camera capture device information * @ctx: hardware context data - * @vfd: video device node for camera capture mode * @subdev: subdev exposing the FIMC processing block + * @ve: exynos video device entity structure * @vd_pad: fimc video capture node pad * @sd_pads: fimc video processing block pads * @ci_fmt: image format at the FIMC camera input (and the scaler output) @@ -298,15 +300,16 @@ struct fimc_m2m_device { * @frame_count: the frame counter for statistics * @reqbufs_count: the number of buffers requested in REQBUFS ioctl * @input_index: input (camera sensor) index - * @refcnt: driver's private reference counter * @input: capture input type, grp_id of the attached subdev * @user_subdev_api: true if subdevs are not configured by the host driver + * @inh_sensor_ctrls: a flag indicating v4l2 controls are inherited from + * an image sensor subdev */ struct fimc_vid_cap { struct fimc_ctx *ctx; struct vb2_alloc_ctx *alloc_ctx; - struct video_device vfd; struct v4l2_subdev subdev; + struct exynos_video_entity ve; struct media_pad vd_pad; struct media_pad sd_pads[FIMC_SD_PADS_NUM]; struct v4l2_mbus_framefmt ci_fmt; @@ -321,9 +324,9 @@ struct fimc_vid_cap { unsigned int reqbufs_count; bool streaming; int input_index; - int refcnt; u32 input; bool user_subdev_api; + bool inh_sensor_ctrls; }; /** @@ -434,8 +437,6 @@ struct fimc_dev { struct fimc_vid_cap vid_cap; unsigned long state; struct vb2_alloc_ctx *alloc_ctx; - struct fimc_pipeline pipeline; - const struct fimc_pipeline_ops *pipeline_ops; }; /** @@ -620,8 +621,6 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, struct v4l2_fmtdesc *f); -void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap, - unsigned int caps); int fimc_ctrls_create(struct fimc_ctx *ctx); void fimc_ctrls_delete(struct fimc_ctx *ctx); void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active); diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c index c397777d7cb..617a798d923 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c +++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c @@ -96,7 +96,7 @@ static int fimc_is_i2c_resume(struct device *dev) return clk_prepare_enable(isp_i2c->clock); } -UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, +static UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend, fimc_is_i2c_resume, NULL); static const struct of_device_id fimc_is_i2c_of_match[] = { diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c index 53fe2a2b4db..c7e7f694c6e 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-param.c +++ b/drivers/media/platform/exynos4-is/fimc-is-param.c @@ -38,7 +38,7 @@ static void __hw_param_copy(void *dst, void *src) memcpy(dst, src, FIMC_IS_PARAM_MAX_SIZE); } -void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) +static void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) { struct param_global_shotmode *dst, *src; @@ -47,7 +47,7 @@ void __fimc_is_hw_update_param_global_shotmode(struct fimc_is *is) __hw_param_copy(dst, src); } -void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) +static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is) { struct param_sensor_framerate *dst, *src; @@ -168,8 +168,8 @@ unsigned int __get_pending_param_count(struct fimc_is *is) unsigned int count; spin_lock_irqsave(&is->slock, flags); - count = hweight32(config->p_region_index1); - count += hweight32(config->p_region_index2); + count = hweight32(config->p_region_index[0]); + count += hweight32(config->p_region_index[1]); spin_unlock_irqrestore(&is->slock, flags); return count; @@ -177,31 +177,30 @@ unsigned int __get_pending_param_count(struct fimc_is *is) int __is_hw_update_params(struct fimc_is *is) { - unsigned long *p_index1, *p_index2; + unsigned long *p_index; int i, id, ret = 0; id = is->config_index; - p_index1 = &is->config[id].p_region_index1; - p_index2 = &is->config[id].p_region_index2; + p_index = &is->config[id].p_region_index[0]; - if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index1)) + if (test_bit(PARAM_GLOBAL_SHOTMODE, p_index)) __fimc_is_hw_update_param_global_shotmode(is); - if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) + if (test_bit(PARAM_SENSOR_FRAME_RATE, p_index)) __fimc_is_hw_update_param_sensor_framerate(is); for (i = PARAM_ISP_CONTROL; i < PARAM_DRC_CONTROL; i++) { - if (test_bit(i, p_index1)) + if (test_bit(i, p_index)) ret = __fimc_is_hw_update_param(is, i); } for (i = PARAM_DRC_CONTROL; i < PARAM_SCALERC_CONTROL; i++) { - if (test_bit(i, p_index1)) + if (test_bit(i, p_index)) ret = __fimc_is_hw_update_param(is, i); } for (i = PARAM_FD_CONTROL; i <= PARAM_FD_CONFIG; i++) { - if (test_bit((i - 32), p_index2)) + if (test_bit(i, p_index)) ret = __fimc_is_hw_update_param(is, i); } @@ -243,7 +242,7 @@ void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf) fd->otf_input.height = mf->height; if (test_bit(PARAM_ISP_OTF_INPUT, - &is->config[index].p_region_index1)) + &is->config[index].p_region_index[0])) return; /* Update field */ @@ -368,7 +367,7 @@ void __is_set_isp_adjust(struct fimc_is *is, u32 cmd, u32 val) unsigned long *p_index; struct isp_param *isp; - p_index = &is->config[index].p_region_index1; + p_index = &is->config[index].p_region_index[0]; isp = &is->config[index].isp; switch (cmd) { @@ -415,7 +414,7 @@ void __is_set_isp_metering(struct fimc_is *is, u32 id, u32 val) struct isp_param *isp; unsigned long *p_index; - p_index = &is->config[index].p_region_index1; + p_index = &is->config[index].p_region_index[0]; isp = &is->config[index].isp; switch (id) { @@ -476,7 +475,7 @@ void __is_set_fd_control(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->control.cmd = val; @@ -491,7 +490,7 @@ void __is_set_fd_config_maxface(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.max_number = val; @@ -511,7 +510,7 @@ void __is_set_fd_config_rollangle(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.roll_angle = val; @@ -531,7 +530,7 @@ void __is_set_fd_config_yawangle(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.yaw_angle = val; @@ -551,7 +550,7 @@ void __is_set_fd_config_smilemode(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.smile_mode = val; @@ -571,7 +570,7 @@ void __is_set_fd_config_blinkmode(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.blink_mode = val; @@ -591,7 +590,7 @@ void __is_set_fd_config_eyedetect(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.eye_detect = val; @@ -611,7 +610,7 @@ void __is_set_fd_config_mouthdetect(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.mouth_detect = val; @@ -631,7 +630,7 @@ void __is_set_fd_config_orientation(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.orientation = val; @@ -651,7 +650,7 @@ void __is_set_fd_config_orientation_val(struct fimc_is *is, u32 val) struct fd_param *fd; unsigned long *p_index; - p_index = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[1]; fd = &is->config[index].fd; fd->config.orientation_value = val; @@ -672,7 +671,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) struct isp_param *isp; struct drc_param *drc; struct fd_param *fd; - unsigned long *p_index1, *p_index2; + unsigned long *p_index; unsigned int index; index = is->config_index; @@ -681,8 +680,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) isp = &is->config[index].isp; drc = &is->config[index].drc; fd = &is->config[index].fd; - p_index1 = &is->config[index].p_region_index1; - p_index2 = &is->config[index].p_region_index2; + p_index = &is->config[index].p_region_index[0]; /* Global */ global->shotmode.cmd = 1; @@ -695,7 +693,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) fimc_is_set_param_bit(is, PARAM_ISP_CONTROL); isp->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; - if (!test_bit(PARAM_ISP_OTF_INPUT, p_index1)) { + if (!test_bit(PARAM_ISP_OTF_INPUT, p_index)) { isp->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; isp->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT); @@ -738,20 +736,20 @@ void fimc_is_set_initial_params(struct fimc_is *is) isp->aa.target = ISP_AA_TARGET_AE | ISP_AA_TARGET_AWB; fimc_is_set_param_bit(is, PARAM_ISP_AA); - if (!test_bit(PARAM_ISP_FLASH, p_index1)) + if (!test_bit(PARAM_ISP_FLASH, p_index)) __is_set_isp_flash(is, ISP_FLASH_COMMAND_DISABLE, ISP_FLASH_REDEYE_DISABLE); - if (!test_bit(PARAM_ISP_AWB, p_index1)) + if (!test_bit(PARAM_ISP_AWB, p_index)) __is_set_isp_awb(is, ISP_AWB_COMMAND_AUTO, 0); - if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index1)) + if (!test_bit(PARAM_ISP_IMAGE_EFFECT, p_index)) __is_set_isp_effect(is, ISP_IMAGE_EFFECT_DISABLE); - if (!test_bit(PARAM_ISP_ISO, p_index1)) + if (!test_bit(PARAM_ISP_ISO, p_index)) __is_set_isp_iso(is, ISP_ISO_COMMAND_AUTO, 0); - if (!test_bit(PARAM_ISP_ADJUST, p_index1)) { + if (!test_bit(PARAM_ISP_ADJUST, p_index)) { __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_CONTRAST, 0); __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_SATURATION, 0); @@ -762,7 +760,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) __is_set_isp_adjust(is, ISP_ADJUST_COMMAND_MANUAL_HUE, 0); } - if (!test_bit(PARAM_ISP_METERING, p_index1)) { + if (!test_bit(PARAM_ISP_METERING, p_index)) { __is_set_isp_metering(is, 0, ISP_METERING_COMMAND_CENTER); __is_set_isp_metering(is, 1, 0); __is_set_isp_metering(is, 2, 0); @@ -770,11 +768,11 @@ void fimc_is_set_initial_params(struct fimc_is *is) __is_set_isp_metering(is, 4, 0); } - if (!test_bit(PARAM_ISP_AFC, p_index1)) + if (!test_bit(PARAM_ISP_AFC, p_index)) __is_set_isp_afc(is, ISP_AFC_COMMAND_AUTO, 0); isp->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; - if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index1)) { + if (!test_bit(PARAM_ISP_OTF_OUTPUT, p_index)) { isp->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; isp->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; fimc_is_set_param_bit(is, PARAM_ISP_OTF_OUTPUT); @@ -784,7 +782,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) isp->otf_output.order = 0; isp->otf_output.err = OTF_OUTPUT_ERROR_NONE; - if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index1)) { + if (!test_bit(PARAM_ISP_DMA1_OUTPUT, p_index)) { isp->dma1_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; isp->dma1_output.width = 0; isp->dma1_output.height = 0; @@ -800,7 +798,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) fimc_is_set_param_bit(is, PARAM_ISP_DMA1_OUTPUT); } - if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index1)) { + if (!test_bit(PARAM_ISP_DMA2_OUTPUT, p_index)) { isp->dma2_output.cmd = DMA_OUTPUT_COMMAND_DISABLE; isp->dma2_output.width = 0; isp->dma2_output.height = 0; @@ -817,7 +815,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) } /* Sensor */ - if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index1)) { + if (!test_bit(PARAM_SENSOR_FRAME_RATE, p_index)) { if (is->config_index == 0) __is_set_sensor(is, 0); } @@ -827,7 +825,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) __is_set_drc_control(is, CONTROL_BYPASS_ENABLE); drc->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; - if (!test_bit(PARAM_DRC_OTF_INPUT, p_index1)) { + if (!test_bit(PARAM_DRC_OTF_INPUT, p_index)) { drc->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; drc->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; fimc_is_set_param_bit(is, PARAM_DRC_OTF_INPUT); @@ -850,7 +848,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) fimc_is_set_param_bit(is, PARAM_DRC_DMA_INPUT); drc->otf_output.cmd = OTF_OUTPUT_COMMAND_ENABLE; - if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index1)) { + if (!test_bit(PARAM_DRC_OTF_OUTPUT, p_index)) { drc->otf_output.width = DEFAULT_PREVIEW_STILL_WIDTH; drc->otf_output.height = DEFAULT_PREVIEW_STILL_HEIGHT; fimc_is_set_param_bit(is, PARAM_DRC_OTF_OUTPUT); @@ -865,7 +863,7 @@ void fimc_is_set_initial_params(struct fimc_is *is) fd->control.bypass = CONTROL_BYPASS_DISABLE; fd->otf_input.cmd = OTF_INPUT_COMMAND_ENABLE; - if (!test_bit((PARAM_FD_OTF_INPUT - 32), p_index2)) { + if (!test_bit(PARAM_FD_OTF_INPUT, p_index)) { fd->otf_input.width = DEFAULT_PREVIEW_STILL_WIDTH; fd->otf_input.height = DEFAULT_PREVIEW_STILL_HEIGHT; fimc_is_set_param_bit(is, PARAM_FD_OTF_INPUT); diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c index d05eaa2c849..63c68ec7cfa 100644 --- a/drivers/media/platform/exynos4-is/fimc-is-regs.c +++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c @@ -89,8 +89,8 @@ int fimc_is_hw_set_param(struct fimc_is *is) mcuctl_write(is->config_index, is, MCUCTL_REG_ISSR(2)); mcuctl_write(param_count, is, MCUCTL_REG_ISSR(3)); - mcuctl_write(config->p_region_index1, is, MCUCTL_REG_ISSR(4)); - mcuctl_write(config->p_region_index2, is, MCUCTL_REG_ISSR(5)); + mcuctl_write(config->p_region_index[0], is, MCUCTL_REG_ISSR(4)); + mcuctl_write(config->p_region_index[1], is, MCUCTL_REG_ISSR(5)); fimc_is_hw_set_intgr0_gd0(is); return 0; diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 0741945b79e..967f6a93934 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -129,7 +129,7 @@ static int fimc_is_setup_clocks(struct fimc_is *is) ATCLK_MCUISP_FREQUENCY); } -int fimc_is_enable_clocks(struct fimc_is *is) +static int fimc_is_enable_clocks(struct fimc_is *is) { int i, ret; @@ -149,7 +149,7 @@ int fimc_is_enable_clocks(struct fimc_is *is) return 0; } -void fimc_is_disable_clocks(struct fimc_is *is) +static void fimc_is_disable_clocks(struct fimc_is *is) { int i; @@ -527,8 +527,8 @@ static void fimc_is_general_irq_handler(struct fimc_is *is) break; case HIC_SET_PARAMETER: - is->config[is->config_index].p_region_index1 = 0; - is->config[is->config_index].p_region_index2 = 0; + is->config[is->config_index].p_region_index[0] = 0; + is->config[is->config_index].p_region_index[1] = 0; set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); pr_debug("HIC_SET_PARAMETER\n"); break; @@ -587,8 +587,8 @@ static void fimc_is_general_irq_handler(struct fimc_is *is) switch (is->i2h_cmd.args[0]) { case HIC_SET_PARAMETER: - is->config[is->config_index].p_region_index1 = 0; - is->config[is->config_index].p_region_index2 = 0; + is->config[is->config_index].p_region_index[0] = 0; + is->config[is->config_index].p_region_index[1] = 0; set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); break; } diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/exynos4-is/fimc-is.h index d7db133b493..61bb0127e19 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.h +++ b/drivers/media/platform/exynos4-is/fimc-is.h @@ -33,8 +33,8 @@ #define FIMC_IS_DRV_NAME "exynos4-fimc-is" -#define FIMC_IS_FW_FILENAME "fimc_is_fw.bin" -#define FIMC_IS_SETFILE_6A3 "setfile.bin" +#define FIMC_IS_FW_FILENAME "exynos4_fimc_is_fw.bin" +#define FIMC_IS_SETFILE_6A3 "exynos4_s5k6a3_setfile.bin" #define FIMC_IS_FW_LOAD_TIMEOUT 1000 /* ms */ #define FIMC_IS_POWER_ON_TIMEOUT 1000 /* us */ @@ -225,8 +225,7 @@ struct chain_config { struct drc_param drc; struct fd_param fd; - unsigned long p_region_index1; - unsigned long p_region_index2; + unsigned long p_region_index[2]; }; /** @@ -302,10 +301,7 @@ static inline void fimc_is_set_param_bit(struct fimc_is *is, int num) { struct chain_config *cfg = &is->config[is->config_index]; - if (num >= 32) - set_bit(num - 32, &cfg->p_region_index2); - else - set_bit(num, &cfg->p_region_index1); + set_bit(num, &cfg->p_region_index[0]); } static inline void fimc_is_set_param_ctrl_cmd(struct fimc_is *is, int cmd) diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c index 7ede30b5910..cf520a7d7f7 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.c +++ b/drivers/media/platform/exynos4-is/fimc-isp.c @@ -30,8 +30,8 @@ #include "fimc-is-regs.h" #include "fimc-is.h" -static int debug; -module_param_named(debug_isp, debug, int, S_IRUGO | S_IWUSR); +int fimc_isp_debug; +module_param_named(debug_isp, fimc_isp_debug, int, S_IRUGO | S_IWUSR); static const struct fimc_fmt fimc_isp_formats[FIMC_ISP_NUM_FORMATS] = { { @@ -128,57 +128,70 @@ static int fimc_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct fimc_isp *isp = v4l2_get_subdevdata(sd); - struct fimc_is *is = fimc_isp_to_is(isp); struct v4l2_mbus_framefmt *mf = &fmt->format; - struct v4l2_mbus_framefmt cur_fmt; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); - fmt->format = *mf; + *mf = *v4l2_subdev_get_try_format(fh, fmt->pad); return 0; } mf->colorspace = V4L2_COLORSPACE_SRGB; mutex_lock(&isp->subdev_lock); - __is_get_frame_size(is, &cur_fmt); if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { - /* full camera input frame size */ - mf->width = cur_fmt.width + FIMC_ISP_CAC_MARGIN_WIDTH; - mf->height = cur_fmt.height + FIMC_ISP_CAC_MARGIN_HEIGHT; - mf->code = V4L2_MBUS_FMT_SGRBG10_1X10; + /* ISP OTF input image format */ + *mf = isp->sink_fmt; } else { - /* crop size */ - mf->width = cur_fmt.width; - mf->height = cur_fmt.height; - mf->code = V4L2_MBUS_FMT_YUV10_1X30; + /* ISP OTF output image format */ + *mf = isp->src_fmt; + + if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) { + mf->colorspace = V4L2_COLORSPACE_JPEG; + mf->code = V4L2_MBUS_FMT_YUV10_1X30; + } } mutex_unlock(&isp->subdev_lock); - v4l2_dbg(1, debug, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", - __func__, fmt->pad, mf->code, mf->width, mf->height); + isp_dbg(1, sd, "%s: pad%d: fmt: 0x%x, %dx%d\n", __func__, + fmt->pad, mf->code, mf->width, mf->height); return 0; } static void __isp_subdev_try_format(struct fimc_isp *isp, - struct v4l2_subdev_format *fmt) + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) { struct v4l2_mbus_framefmt *mf = &fmt->format; + struct v4l2_mbus_framefmt *format; + + mf->colorspace = V4L2_COLORSPACE_SRGB; if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { v4l_bound_align_image(&mf->width, FIMC_ISP_SINK_WIDTH_MIN, FIMC_ISP_SINK_WIDTH_MAX, 0, &mf->height, FIMC_ISP_SINK_HEIGHT_MIN, FIMC_ISP_SINK_HEIGHT_MAX, 0, 0); - isp->subdev_fmt = *mf; + mf->code = V4L2_MBUS_FMT_SGRBG10_1X10; } else { + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + format = v4l2_subdev_get_try_format(fh, + FIMC_ISP_SD_PAD_SINK); + else + format = &isp->sink_fmt; + /* Allow changing format only on sink pad */ - mf->width = isp->subdev_fmt.width - FIMC_ISP_CAC_MARGIN_WIDTH; - mf->height = isp->subdev_fmt.height - FIMC_ISP_CAC_MARGIN_HEIGHT; - mf->code = isp->subdev_fmt.code; + mf->width = format->width - FIMC_ISP_CAC_MARGIN_WIDTH; + mf->height = format->height - FIMC_ISP_CAC_MARGIN_HEIGHT; + + if (fmt->pad == FIMC_ISP_SD_PAD_SRC_FIFO) { + mf->code = V4L2_MBUS_FMT_YUV10_1X30; + mf->colorspace = V4L2_COLORSPACE_JPEG; + } else { + mf->code = format->code; + } } } @@ -191,27 +204,50 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &fmt->format; int ret = 0; - v4l2_dbg(1, debug, sd, "%s: pad%d: code: 0x%x, %dx%d\n", + isp_dbg(1, sd, "%s: pad%d: code: 0x%x, %dx%d\n", __func__, fmt->pad, mf->code, mf->width, mf->height); - mf->colorspace = V4L2_COLORSPACE_SRGB; - mutex_lock(&isp->subdev_lock); - __isp_subdev_try_format(isp, fmt); + __isp_subdev_try_format(isp, fh, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { mf = v4l2_subdev_get_try_format(fh, fmt->pad); *mf = fmt->format; - mutex_unlock(&isp->subdev_lock); - return 0; + + /* Propagate format to the source pads */ + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + struct v4l2_subdev_format format = *fmt; + unsigned int pad; + + for (pad = FIMC_ISP_SD_PAD_SRC_FIFO; + pad < FIMC_ISP_SD_PADS_NUM; pad++) { + format.pad = pad; + __isp_subdev_try_format(isp, fh, &format); + mf = v4l2_subdev_get_try_format(fh, pad); + *mf = format.format; + } + } + } else { + if (sd->entity.stream_count == 0) { + if (fmt->pad == FIMC_ISP_SD_PAD_SINK) { + struct v4l2_subdev_format format = *fmt; + + isp->sink_fmt = *mf; + + format.pad = FIMC_ISP_SD_PAD_SRC_DMA; + __isp_subdev_try_format(isp, fh, &format); + + isp->src_fmt = format.format; + __is_set_frame_size(is, &isp->src_fmt); + } else { + isp->src_fmt = *mf; + } + } else { + ret = -EBUSY; + } } - if (sd->entity.stream_count == 0) - __is_set_frame_size(is, mf); - else - ret = -EBUSY; mutex_unlock(&isp->subdev_lock); - return ret; } @@ -221,7 +257,7 @@ static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) struct fimc_is *is = fimc_isp_to_is(isp); int ret; - v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); + isp_dbg(1, sd, "%s: on: %d\n", __func__, on); if (!test_bit(IS_ST_INIT_DONE, &is->state)) return -EBUSY; @@ -235,8 +271,8 @@ static int fimc_isp_subdev_s_stream(struct v4l2_subdev *sd, int on) return ret; } - v4l2_dbg(1, debug, sd, "changing mode to %d\n", - is->config_index); + isp_dbg(1, sd, "changing mode to %d\n", is->config_index); + ret = fimc_is_itf_mode_change(is); if (ret) return -EINVAL; @@ -317,8 +353,8 @@ static int fimc_isp_subdev_s_power(struct v4l2_subdev *sd, int on) clear_bit(IS_ST_PWR_ON, &is->state); clear_bit(IS_ST_INIT_DONE, &is->state); is->state = 0; - is->config[is->config_index].p_region_index1 = 0; - is->config[is->config_index].p_region_index2 = 0; + is->config[is->config_index].p_region_index[0] = 0; + is->config[is->config_index].p_region_index[1] = 0; set_bit(IS_ST_IDLE, &is->state); wmb(); } @@ -609,6 +645,22 @@ static const struct v4l2_ctrl_ops fimc_isp_ctrl_ops = { .s_ctrl = fimc_is_s_ctrl, }; +static void __isp_subdev_set_default_format(struct fimc_isp *isp) +{ + struct fimc_is *is = fimc_isp_to_is(isp); + + isp->sink_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH + + FIMC_ISP_CAC_MARGIN_WIDTH; + isp->sink_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT + + FIMC_ISP_CAC_MARGIN_HEIGHT; + isp->sink_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10; + + isp->src_fmt.width = DEFAULT_PREVIEW_STILL_WIDTH; + isp->src_fmt.height = DEFAULT_PREVIEW_STILL_HEIGHT; + isp->src_fmt.code = V4L2_MBUS_FMT_SGRBG10_1X10; + __is_set_frame_size(is, &isp->src_fmt); +} + int fimc_isp_subdev_create(struct fimc_isp *isp) { const struct v4l2_ctrl_ops *ops = &fimc_isp_ctrl_ops; @@ -689,6 +741,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp) sd->entity.ops = &fimc_is_subdev_media_ops; v4l2_set_subdevdata(sd, isp); + __isp_subdev_set_default_format(isp); + return 0; } diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/exynos4-is/fimc-isp.h index 800aba7ab4a..03bf95ab017 100644 --- a/drivers/media/platform/exynos4-is/fimc-isp.h +++ b/drivers/media/platform/exynos4-is/fimc-isp.h @@ -26,6 +26,11 @@ #include <media/v4l2-mediabus.h> #include <media/s5p_fimc.h> +extern int fimc_isp_debug; + +#define isp_dbg(level, dev, fmt, arg...) \ + v4l2_dbg(level, fimc_isp_debug, dev, fmt, ## arg) + /* FIXME: revisit these constraints */ #define FIMC_ISP_SINK_WIDTH_MIN (16 + 8) #define FIMC_ISP_SINK_HEIGHT_MIN (12 + 8) @@ -118,7 +123,6 @@ struct fimc_is_video { unsigned int frame_count; unsigned int reqbufs_count; int streaming; - unsigned long payload[FIMC_ISP_MAX_PLANES]; const struct fimc_fmt *format; }; @@ -128,15 +132,9 @@ struct fimc_is_video { * @alloc_ctx: videobuf2 memory allocator context * @subdev: ISP v4l2_subdev * @subdev_pads: the ISP subdev media pads - * @ctrl_handler: v4l2 controls handler * @test_pattern: test pattern controls - * @pipeline: video capture pipeline data structure + * @ctrls: v4l2 controls structure * @video_lock: mutex serializing video device and the subdev operations - * @fmt: pointer to color format description structure - * @payload: image size in bytes (w x h x bpp) - * @inp_frame: camera input frame structure - * @out_frame: DMA output frame structure - * @source_subdev_grp_id: group id of remote source subdev * @cac_margin_x: horizontal CAC margin in pixels * @cac_margin_y: vertical CAC margin in pixels * @state: driver state flags @@ -147,17 +145,14 @@ struct fimc_isp { struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; struct media_pad subdev_pads[FIMC_ISP_SD_PADS_NUM]; - struct v4l2_mbus_framefmt subdev_fmt; + struct v4l2_mbus_framefmt src_fmt; + struct v4l2_mbus_framefmt sink_fmt; struct v4l2_ctrl *test_pattern; struct fimc_isp_ctrls ctrls; struct mutex video_lock; struct mutex subdev_lock; - struct fimc_isp_frame inp_frame; - struct fimc_isp_frame out_frame; - unsigned int source_subdev_grp_id; - unsigned int cac_margin_x; unsigned int cac_margin_y; diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/exynos4-is/fimc-lite-reg.c index 8cc0d39a2fe..72a343e3b5e 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.c @@ -2,15 +2,16 @@ * Register interface file for EXYNOS FIMC-LITE (camera interface) driver * * Copyright (C) 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki <s.nawrocki@samsung.com> + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> * * 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 <linux/io.h> +#include <linux/bitops.h> #include <linux/delay.h> +#include <linux/io.h> #include <media/s5p_fimc.h> #include "fimc-lite-reg.h" @@ -68,7 +69,8 @@ void flite_hw_set_interrupt_mask(struct fimc_lite *dev) if (atomic_read(&dev->out_path) == FIMC_IO_DMA) { intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | FLITE_REG_CIGCTRL_IRQ_LASTEN | - FLITE_REG_CIGCTRL_IRQ_STARTEN; + FLITE_REG_CIGCTRL_IRQ_STARTEN | + FLITE_REG_CIGCTRL_IRQ_ENDEN; } else { /* An output to the FIMC-IS */ intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | @@ -137,7 +139,7 @@ void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) } if (i == 0 && src_pixfmt_map[i][0] != pixelcode) { - v4l2_err(&dev->vfd, + v4l2_err(&dev->ve.vdev, "Unsupported pixel code, falling back to %#08x\n", src_pixfmt_map[i][0]); } @@ -215,6 +217,18 @@ void flite_hw_set_camera_bus(struct fimc_lite *dev, flite_hw_set_camera_port(dev, si->mux_id); } +static void flite_hw_set_pack12(struct fimc_lite *dev, int on) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); + + cfg &= ~FLITE_REG_CIODMAFMT_PACK12; + + if (on) + cfg |= FLITE_REG_CIODMAFMT_PACK12; + + writel(cfg, dev->regs + FLITE_REG_CIODMAFMT); +} + static void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) { static const u32 pixcode[4][2] = { @@ -250,6 +264,38 @@ void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) writel(cfg, dev->regs + FLITE_REG_CIOOFF); } +void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf) +{ + unsigned int index; + u32 cfg; + + if (dev->dd->max_dma_bufs == 1) + index = 0; + else + index = buf->index; + + if (index == 0) + writel(buf->paddr, dev->regs + FLITE_REG_CIOSA); + else + writel(buf->paddr, dev->regs + FLITE_REG_CIOSAN(index - 1)); + + cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ); + cfg |= BIT(index); + writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ); +} + +void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index) +{ + u32 cfg; + + if (dev->dd->max_dma_bufs == 1) + index = 0; + + cfg = readl(dev->regs + FLITE_REG_CIFCNTSEQ); + cfg &= ~BIT(index); + writel(cfg, dev->regs + FLITE_REG_CIFCNTSEQ); +} + /* Enable/disable output DMA, set output pixel size and offsets (composition) */ void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, bool enable) @@ -267,6 +313,7 @@ void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, flite_hw_set_out_order(dev, f); flite_hw_set_dma_window(dev, f); + flite_hw_set_pack12(dev, 0); } void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.h b/drivers/media/platform/exynos4-is/fimc-lite-reg.h index 390383941c1..10a7d7bbcc2 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite-reg.h +++ b/drivers/media/platform/exynos4-is/fimc-lite-reg.h @@ -120,6 +120,9 @@ /* b0: 1 - camera B, 0 - camera A */ #define FLITE_REG_CIGENERAL_CAM_B (1 << 0) +#define FLITE_REG_CIFCNTSEQ 0x100 +#define FLITE_REG_CIOSAN(x) (0x200 + (4 * (x))) + /* ---------------------------------------------------------------------------- * Function declarations */ @@ -142,9 +145,12 @@ void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f); void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on); void flite_hw_dump_regs(struct fimc_lite *dev, const char *label); +void flite_hw_set_dma_buffer(struct fimc_lite *dev, struct flite_buffer *buf); +void flite_hw_mask_dma_buffer(struct fimc_lite *dev, u32 index); -static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr) +static inline void flite_hw_set_dma_buf_mask(struct fimc_lite *dev, u32 mask) { - writel(paddr, dev->regs + FLITE_REG_CIOSA); + writel(mask, dev->regs + FLITE_REG_CIFCNTSEQ); } + #endif /* FIMC_LITE_REG_H */ diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c index 14bb7bc8adb..08fbfedea90 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.c +++ b/drivers/media/platform/exynos4-is/fimc-lite.c @@ -1,8 +1,8 @@ /* * Samsung EXYNOS FIMC-LITE (camera host interface) driver * - * Copyright (C) 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki <s.nawrocki@samsung.com> + * Copyright (C) 2012 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> * * 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 @@ -32,6 +32,7 @@ #include <media/videobuf2-dma-contig.h> #include <media/s5p_fimc.h> +#include "common.h" #include "fimc-core.h" #include "fimc-lite.h" #include "fimc-lite-reg.h" @@ -43,6 +44,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_JPEG, .depth = { 16 }, .color = FIMC_FMT_YCBYCR422, .memplanes = 1, @@ -51,6 +53,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "YUV 4:2:2 packed, CbYCrY", .fourcc = V4L2_PIX_FMT_UYVY, + .colorspace = V4L2_COLORSPACE_JPEG, .depth = { 16 }, .color = FIMC_FMT_CBYCRY422, .memplanes = 1, @@ -59,6 +62,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "YUV 4:2:2 packed, CrYCbY", .fourcc = V4L2_PIX_FMT_VYUY, + .colorspace = V4L2_COLORSPACE_JPEG, .depth = { 16 }, .color = FIMC_FMT_CRYCBY422, .memplanes = 1, @@ -67,6 +71,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, + .colorspace = V4L2_COLORSPACE_JPEG, .depth = { 16 }, .color = FIMC_FMT_YCRYCB422, .memplanes = 1, @@ -75,6 +80,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "RAW8 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG8, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = { 8 }, .color = FIMC_FMT_RAW8, .memplanes = 1, @@ -83,6 +89,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "RAW10 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG10, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = { 10 }, .color = FIMC_FMT_RAW10, .memplanes = 1, @@ -91,6 +98,7 @@ static const struct fimc_fmt fimc_lite_formats[] = { }, { .name = "RAW12 (GRBG)", .fourcc = V4L2_PIX_FMT_SGRBG12, + .colorspace = V4L2_COLORSPACE_SRGB, .depth = { 12 }, .color = FIMC_FMT_RAW12, .memplanes = 1, @@ -131,30 +139,6 @@ static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, return def_fmt; } -/* Called with the media graph mutex held or @me stream_count > 0. */ -static struct v4l2_subdev *__find_remote_sensor(struct media_entity *me) -{ - struct media_pad *pad = &me->pads[0]; - struct v4l2_subdev *sd; - - while (pad->flags & MEDIA_PAD_FL_SINK) { - /* source pad */ - pad = media_entity_remote_source(pad); - if (pad == NULL || - media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) - break; - - sd = media_entity_to_v4l2_subdev(pad->entity); - - if (sd->grp_id == GRP_ID_FIMC_IS_SENSOR || - sd->grp_id == GRP_ID_SENSOR) - return sd; - /* sink pad */ - pad = &sd->entity.pads[0]; - } - return NULL; -} - static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) { struct fimc_source_info *si; @@ -176,6 +160,7 @@ static int fimc_lite_hw_init(struct fimc_lite *fimc, bool isp_output) flite_hw_set_camera_bus(fimc, si); flite_hw_set_source_format(fimc, &fimc->inp_frame); flite_hw_set_window_offset(fimc, &fimc->inp_frame); + flite_hw_set_dma_buf_mask(fimc, 0); flite_hw_set_output_dma(fimc, &fimc->out_frame, !isp_output); flite_hw_set_interrupt_mask(fimc); flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); @@ -233,7 +218,7 @@ static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) if (!streaming) return 0; - return fimc_pipeline_call(fimc, set_stream, &fimc->pipeline, 0); + return fimc_pipeline_call(&fimc->ve, set_stream, 0); } static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend) @@ -299,19 +284,23 @@ static irqreturn_t flite_irq_handler(int irq, void *priv) if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && test_bit(ST_FLITE_RUN, &fimc->state) && - !list_empty(&fimc->active_buf_q) && !list_empty(&fimc->pending_buf_q)) { + vbuf = fimc_lite_pending_queue_pop(fimc); + flite_hw_set_dma_buffer(fimc, vbuf); + fimc_lite_active_queue_add(fimc, vbuf); + } + + if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMEND) && + test_bit(ST_FLITE_RUN, &fimc->state) && + !list_empty(&fimc->active_buf_q)) { vbuf = fimc_lite_active_queue_pop(fimc); ktime_get_ts(&ts); tv = &vbuf->vb.v4l2_buf.timestamp; tv->tv_sec = ts.tv_sec; tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; vbuf->vb.v4l2_buf.sequence = fimc->frame_count++; + flite_hw_mask_dma_buffer(fimc, vbuf->index); vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); - - vbuf = fimc_lite_pending_queue_pop(fimc); - flite_hw_set_output_addr(fimc, vbuf->paddr); - fimc_lite_active_queue_add(fimc, vbuf); } if (test_bit(ST_FLITE_CONFIG, &fimc->state)) @@ -330,10 +319,16 @@ done: static int start_streaming(struct vb2_queue *q, unsigned int count) { struct fimc_lite *fimc = q->drv_priv; + unsigned long flags; int ret; + spin_lock_irqsave(&fimc->slock, flags); + + fimc->buf_index = 0; fimc->frame_count = 0; + spin_unlock_irqrestore(&fimc->slock, flags); + ret = fimc_lite_hw_init(fimc, false); if (ret) { fimc_lite_reinit(fimc, false); @@ -347,8 +342,7 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) flite_hw_capture_start(fimc); if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + fimc_pipeline_call(&fimc->ve, set_stream, 1); } if (debug > 0) flite_hw_dump_regs(fimc, __func__); @@ -415,7 +409,7 @@ static int buffer_prepare(struct vb2_buffer *vb) unsigned long size = fimc->payload[i]; if (vb2_plane_size(vb, i) < size) { - v4l2_err(&fimc->vfd, + v4l2_err(&fimc->ve.vdev, "User buffer too small (%ld < %ld)\n", vb2_plane_size(vb, i), size); return -EINVAL; @@ -436,10 +430,14 @@ static void buffer_queue(struct vb2_buffer *vb) spin_lock_irqsave(&fimc->slock, flags); buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + buf->index = fimc->buf_index++; + if (fimc->buf_index >= fimc->reqbufs_count) + fimc->buf_index = 0; + if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) && !test_bit(ST_FLITE_STREAM, &fimc->state) && list_empty(&fimc->active_buf_q)) { - flite_hw_set_output_addr(fimc, buf->paddr); + flite_hw_set_dma_buffer(fimc, buf); fimc_lite_active_queue_add(fimc, buf); } else { fimc_lite_pending_queue_add(fimc, buf); @@ -452,8 +450,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) - fimc_pipeline_call(fimc, set_stream, - &fimc->pipeline, 1); + fimc_pipeline_call(&fimc->ve, set_stream, 1); return; } spin_unlock_irqrestore(&fimc->slock, flags); @@ -481,11 +478,9 @@ static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) static int fimc_lite_open(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); - struct media_entity *me = &fimc->vfd.entity; + struct media_entity *me = &fimc->ve.vdev.entity; int ret; - mutex_lock(&me->parent->graph_mutex); - mutex_lock(&fimc->lock); if (atomic_read(&fimc->out_path) != FIMC_IO_DMA) { ret = -EBUSY; @@ -505,11 +500,18 @@ static int fimc_lite_open(struct file *file) atomic_read(&fimc->out_path) != FIMC_IO_DMA) goto unlock; - ret = fimc_pipeline_call(fimc, open, &fimc->pipeline, - me, true); + mutex_lock(&me->parent->graph_mutex); + + ret = fimc_pipeline_call(&fimc->ve, open, me, true); + + /* Mark video pipeline ending at this video node as in use. */ + if (ret == 0) + me->use_count++; + + mutex_unlock(&me->parent->graph_mutex); + if (!ret) { fimc_lite_clear_event_counters(fimc); - fimc->ref_count++; goto unlock; } @@ -519,26 +521,29 @@ err_pm: clear_bit(ST_FLITE_IN_USE, &fimc->state); unlock: mutex_unlock(&fimc->lock); - mutex_unlock(&me->parent->graph_mutex); return ret; } static int fimc_lite_release(struct file *file) { struct fimc_lite *fimc = video_drvdata(file); + struct media_entity *entity = &fimc->ve.vdev.entity; mutex_lock(&fimc->lock); if (v4l2_fh_is_singular_file(file) && atomic_read(&fimc->out_path) == FIMC_IO_DMA) { if (fimc->streaming) { - media_entity_pipeline_stop(&fimc->vfd.entity); + media_entity_pipeline_stop(entity); fimc->streaming = false; } - clear_bit(ST_FLITE_IN_USE, &fimc->state); fimc_lite_stop_capture(fimc, false); - fimc_pipeline_call(fimc, close, &fimc->pipeline); - fimc->ref_count--; + fimc_pipeline_call(&fimc->ve, close); + clear_bit(ST_FLITE_IN_USE, &fimc->state); + + mutex_lock(&entity->parent->graph_mutex); + entity->use_count--; + mutex_unlock(&entity->parent->graph_mutex); } vb2_fop_release(file); @@ -562,37 +567,54 @@ static const struct v4l2_file_operations fimc_lite_fops = { * Format and crop negotiation helpers */ -static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, - u32 *width, u32 *height, - u32 *code, u32 *fourcc, int pad) +static const struct fimc_fmt *fimc_lite_subdev_try_fmt(struct fimc_lite *fimc, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) { struct flite_drvdata *dd = fimc->dd; - const struct fimc_fmt *fmt; - unsigned int flags = 0; + struct v4l2_mbus_framefmt *mf = &format->format; + const struct fimc_fmt *fmt = NULL; + + if (format->pad == FLITE_SD_PAD_SINK) { + v4l_bound_align_image(&mf->width, 8, dd->max_width, + ffs(dd->out_width_align) - 1, + &mf->height, 0, dd->max_height, 0, 0); + + fmt = fimc_lite_find_format(NULL, &mf->code, 0, 0); + if (WARN_ON(!fmt)) + return NULL; - if (pad == FLITE_SD_PAD_SINK) { - v4l_bound_align_image(width, 8, dd->max_width, - ffs(dd->out_width_align) - 1, - height, 0, dd->max_height, 0, 0); + mf->colorspace = fmt->colorspace; + mf->code = fmt->mbus_code; } else { - v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, - ffs(dd->out_width_align) - 1, - height, 0, fimc->inp_frame.rect.height, - 0, 0); - flags = fimc->inp_frame.fmt->flags; - } + struct flite_frame *sink = &fimc->inp_frame; + struct v4l2_mbus_framefmt *sink_fmt; + struct v4l2_rect *rect; - fmt = fimc_lite_find_format(fourcc, code, flags, 0); - if (WARN_ON(!fmt)) - return NULL; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + sink_fmt = v4l2_subdev_get_try_format(fh, + FLITE_SD_PAD_SINK); - if (code) - *code = fmt->mbus_code; - if (fourcc) - *fourcc = fmt->fourcc; + mf->code = sink_fmt->code; + mf->colorspace = sink_fmt->colorspace; - v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", - code ? *code : 0, *width, *height); + rect = v4l2_subdev_get_try_crop(fh, + FLITE_SD_PAD_SINK); + } else { + mf->code = sink->fmt->mbus_code; + mf->colorspace = sink->fmt->colorspace; + rect = &sink->rect; + } + + /* Allow changing format only on sink pad */ + mf->width = rect->width; + mf->height = rect->height; + } + + mf->field = V4L2_FIELD_NONE; + + v4l2_dbg(1, debug, &fimc->subdev, "code: %#x (%d), %dx%d\n", + mf->code, mf->colorspace, mf->width, mf->height); return fmt; } @@ -637,13 +659,18 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) /* * Video node ioctl operations */ -static int fimc_vidioc_querycap_capture(struct file *file, void *priv, +static int fimc_lite_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { + struct fimc_lite *fimc = video_drvdata(file); + strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver)); - cap->bus_info[0] = 0; - cap->card[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING; + strlcpy(cap->card, FIMC_LITE_DRV_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(&fimc->pdev->dev)); + + cap->device_caps = V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -679,7 +706,7 @@ static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, pixm->width = frame->f_width; pixm->height = frame->f_height; pixm->field = V4L2_FIELD_NONE; - pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->colorspace = fmt->colorspace; return 0; } @@ -722,7 +749,7 @@ static int fimc_lite_try_fmt(struct fimc_lite *fimc, fmt->depth[0]) / 8; pixm->num_planes = fmt->memplanes; pixm->pixelformat = fmt->fourcc; - pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->colorspace = fmt->colorspace; pixm->field = V4L2_FIELD_NONE; return 0; } @@ -786,7 +813,7 @@ static int fimc_pipeline_validate(struct fimc_lite *fimc) return -EPIPE; } /* Retrieve format at the source pad */ - pad = media_entity_remote_source(pad); + pad = media_entity_remote_pad(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; @@ -810,14 +837,13 @@ static int fimc_lite_streamon(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_lite *fimc = video_drvdata(file); - struct media_entity *entity = &fimc->vfd.entity; - struct fimc_pipeline *p = &fimc->pipeline; + struct media_entity *entity = &fimc->ve.vdev.entity; int ret; if (fimc_lite_active(fimc)) return -EBUSY; - ret = media_entity_pipeline_start(entity, p->m_pipeline); + ret = media_entity_pipeline_start(entity, &fimc->ve.pipe->mp); if (ret < 0) return ret; @@ -825,7 +851,7 @@ static int fimc_lite_streamon(struct file *file, void *priv, if (ret < 0) goto err_p_stop; - fimc->sensor = __find_remote_sensor(&fimc->subdev.entity); + fimc->sensor = fimc_find_remote_sensor(&fimc->subdev.entity); ret = vb2_ioctl_streamon(file, priv, type); if (!ret) { @@ -848,7 +874,7 @@ static int fimc_lite_streamoff(struct file *file, void *priv, if (ret < 0) return ret; - media_entity_pipeline_stop(&fimc->vfd.entity); + media_entity_pipeline_stop(&fimc->ve.vdev.entity); fimc->streaming = false; return 0; } @@ -938,7 +964,7 @@ static int fimc_lite_s_selection(struct file *file, void *fh, } static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { - .vidioc_querycap = fimc_vidioc_querycap_capture, + .vidioc_querycap = fimc_lite_querycap, .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, @@ -972,8 +998,6 @@ static int fimc_lite_link_setup(struct media_entity *entity, __func__, remote->entity->name, local->entity->name, flags, fimc->source_subdev_grp_id); - mutex_lock(&fimc->lock); - switch (local->index) { case FLITE_SD_PAD_SINK: if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) { @@ -1015,7 +1039,6 @@ static int fimc_lite_link_setup(struct media_entity *entity, } mb(); - mutex_unlock(&fimc->lock); return ret; } @@ -1036,6 +1059,15 @@ static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static struct v4l2_mbus_framefmt *__fimc_lite_subdev_get_try_fmt( + struct v4l2_subdev_fh *fh, unsigned int pad) +{ + if (pad != FLITE_SD_PAD_SINK) + pad = FLITE_SD_PAD_SOURCE_DMA; + + return v4l2_subdev_get_try_format(fh, pad); +} + static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) @@ -1045,13 +1077,13 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, struct flite_frame *f = &fimc->inp_frame; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); fmt->format = *mf; return 0; } - mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); + mf->colorspace = f->fmt->colorspace; mf->code = f->fmt->mbus_code; if (fmt->pad == FLITE_SD_PAD_SINK) { @@ -1080,7 +1112,6 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d\n", fmt->pad, mf->code, mf->width, mf->height); - mf->colorspace = V4L2_COLORSPACE_JPEG; mutex_lock(&fimc->lock); if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP && @@ -1091,12 +1122,20 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, return -EBUSY; } - ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height, - &mf->code, NULL, fmt->pad); + ffmt = fimc_lite_subdev_try_fmt(fimc, fh, fmt); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(fh, fmt->pad); + struct v4l2_mbus_framefmt *src_fmt; + + mf = __fimc_lite_subdev_get_try_fmt(fh, fmt->pad); *mf = fmt->format; + + if (fmt->pad == FLITE_SD_PAD_SINK) { + unsigned int pad = FLITE_SD_PAD_SOURCE_DMA; + src_fmt = __fimc_lite_subdev_get_try_fmt(fh, pad); + *src_fmt = *mf; + } + mutex_unlock(&fimc->lock); return 0; } @@ -1114,11 +1153,6 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, source->rect = sink->rect; source->f_width = mf->width; source->f_height = mf->height; - } else { - /* Allow changing format only on sink pad */ - mf->code = sink->fmt->mbus_code; - mf->width = sink->rect.width; - mf->height = sink->rect.height; } mutex_unlock(&fimc->lock); @@ -1207,7 +1241,7 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) * The pipeline links are protected through entity.stream_count * so there is no need to take the media graph mutex here. */ - fimc->sensor = __find_remote_sensor(&sd->entity); + fimc->sensor = fimc_find_remote_sensor(&sd->entity); if (atomic_read(&fimc->out_path) != FIMC_IO_ISP) return -ENOIOCTLCMD; @@ -1252,13 +1286,10 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) { struct fimc_lite *fimc = v4l2_get_subdevdata(sd); struct vb2_queue *q = &fimc->vb_queue; - struct video_device *vfd = &fimc->vfd; + struct video_device *vfd = &fimc->ve.vdev; int ret; memset(vfd, 0, sizeof(*vfd)); - - fimc->inp_frame.fmt = &fimc_lite_formats[0]; - fimc->out_frame.fmt = &fimc_lite_formats[0]; atomic_set(&fimc->out_path, FIMC_IO_DMA); snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", @@ -1295,12 +1326,12 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) return ret; video_set_drvdata(vfd, fimc); - fimc->pipeline_ops = v4l2_get_subdev_hostdata(sd); + fimc->ve.pipe = v4l2_get_subdev_hostdata(sd); ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); if (ret < 0) { media_entity_cleanup(&vfd->entity); - fimc->pipeline_ops = NULL; + fimc->ve.pipe = NULL; return ret; } @@ -1316,11 +1347,15 @@ static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd) if (fimc == NULL) return; - if (video_is_registered(&fimc->vfd)) { - video_unregister_device(&fimc->vfd); - media_entity_cleanup(&fimc->vfd.entity); - fimc->pipeline_ops = NULL; + mutex_lock(&fimc->lock); + + if (video_is_registered(&fimc->ve.vdev)) { + video_unregister_device(&fimc->ve.vdev); + media_entity_cleanup(&fimc->ve.vdev.entity); + fimc->ve.pipe = NULL; } + + mutex_unlock(&fimc->lock); } static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = { @@ -1370,6 +1405,23 @@ static const struct v4l2_ctrl_config fimc_lite_ctrl = { .step = 1, }; +static void fimc_lite_set_default_config(struct fimc_lite *fimc) +{ + struct flite_frame *sink = &fimc->inp_frame; + struct flite_frame *source = &fimc->out_frame; + + sink->fmt = &fimc_lite_formats[0]; + sink->f_width = FLITE_DEFAULT_WIDTH; + sink->f_height = FLITE_DEFAULT_HEIGHT; + + sink->rect.width = FLITE_DEFAULT_WIDTH; + sink->rect.height = FLITE_DEFAULT_HEIGHT; + sink->rect.left = 0; + sink->rect.top = 0; + + *source = *sink; +} + static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) { struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler; @@ -1417,12 +1469,12 @@ static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc) static void fimc_lite_clk_put(struct fimc_lite *fimc) { - if (IS_ERR_OR_NULL(fimc->clock)) + if (IS_ERR(fimc->clock)) return; clk_unprepare(fimc->clock); clk_put(fimc->clock); - fimc->clock = NULL; + fimc->clock = ERR_PTR(-EINVAL); } static int fimc_lite_clk_get(struct fimc_lite *fimc) @@ -1436,7 +1488,7 @@ static int fimc_lite_clk_get(struct fimc_lite *fimc) ret = clk_prepare(fimc->clock); if (ret < 0) { clk_put(fimc->clock); - fimc->clock = NULL; + fimc->clock = ERR_PTR(-EINVAL); } return ret; } @@ -1461,13 +1513,14 @@ static int fimc_lite_probe(struct platform_device *pdev) if (of_id) drv_data = (struct flite_drvdata *)of_id->data; fimc->index = of_alias_get_id(dev->of_node, "fimc-lite"); - } else { - drv_data = fimc_lite_get_drvdata(pdev); - fimc->index = pdev->id; } - if (!drv_data || fimc->index < 0 || fimc->index >= FIMC_LITE_MAX_DEVS) + if (!drv_data || fimc->index >= drv_data->num_instances || + fimc->index < 0) { + dev_err(dev, "Wrong %s node alias\n", + dev->of_node->full_name); return -EINVAL; + } fimc->dd = drv_data; fimc->pdev = pdev; @@ -1514,8 +1567,11 @@ static int fimc_lite_probe(struct platform_device *pdev) ret = PTR_ERR(fimc->alloc_ctx); goto err_pm; } + pm_runtime_put(dev); + fimc_lite_set_default_config(fimc); + dev_dbg(dev, "FIMC-LITE.%d registered successfully\n", fimc->index); return 0; @@ -1565,8 +1621,8 @@ static int fimc_lite_resume(struct device *dev) return 0; INIT_LIST_HEAD(&fimc->active_buf_q); - fimc_pipeline_call(fimc, open, &fimc->pipeline, - &fimc->vfd.entity, false); + fimc_pipeline_call(&fimc->ve, open, + &fimc->ve.vdev.entity, false); fimc_lite_hw_init(fimc, atomic_read(&fimc->out_path) == FIMC_IO_ISP); clear_bit(ST_FLITE_SUSPENDED, &fimc->state); @@ -1592,7 +1648,7 @@ static int fimc_lite_suspend(struct device *dev) if (ret < 0 || !fimc_lite_active(fimc)) return ret; - return fimc_pipeline_call(fimc, close, &fimc->pipeline); + return fimc_pipeline_call(&fimc->ve, close); } #endif /* CONFIG_PM_SLEEP */ @@ -1624,22 +1680,30 @@ static struct flite_drvdata fimc_lite_drvdata_exynos4 = { .out_width_align = 8, .win_hor_offs_align = 2, .out_hor_offs_align = 8, + .max_dma_bufs = 1, + .num_instances = 2, }; -static struct platform_device_id fimc_lite_driver_ids[] = { - { - .name = "exynos-fimc-lite", - .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4, - }, - { /* sentinel */ }, +/* EXYNOS5250 */ +static struct flite_drvdata fimc_lite_drvdata_exynos5 = { + .max_width = 8192, + .max_height = 8192, + .out_width_align = 8, + .win_hor_offs_align = 2, + .out_hor_offs_align = 8, + .max_dma_bufs = 32, + .num_instances = 3, }; -MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); static const struct of_device_id flite_of_match[] = { { .compatible = "samsung,exynos4212-fimc-lite", .data = &fimc_lite_drvdata_exynos4, }, + { + .compatible = "samsung,exynos5250-fimc-lite", + .data = &fimc_lite_drvdata_exynos5, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, flite_of_match); @@ -1647,7 +1711,6 @@ MODULE_DEVICE_TABLE(of, flite_of_match); static struct platform_driver fimc_lite_driver = { .probe = fimc_lite_probe, .remove = fimc_lite_remove, - .id_table = fimc_lite_driver_ids, .driver = { .of_match_table = flite_of_match, .name = FIMC_LITE_DRV_NAME, diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/exynos4-is/fimc-lite.h index 47da5e04924..7428b2d22b5 100644 --- a/drivers/media/platform/exynos4-is/fimc-lite.h +++ b/drivers/media/platform/exynos4-is/fimc-lite.h @@ -27,8 +27,10 @@ #define FIMC_LITE_DRV_NAME "exynos-fimc-lite" #define FLITE_CLK_NAME "flite" -#define FIMC_LITE_MAX_DEVS 2 +#define FIMC_LITE_MAX_DEVS 3 #define FLITE_REQ_BUFS_MIN 2 +#define FLITE_DEFAULT_WIDTH 640 +#define FLITE_DEFAULT_HEIGHT 480 /* Bit index definitions for struct fimc_lite::state */ enum { @@ -48,17 +50,28 @@ enum { #define FLITE_SD_PAD_SOURCE_ISP 2 #define FLITE_SD_PADS_NUM 3 +/** + * struct flite_drvdata - FIMC-LITE IP variant data structure + * @max_width: maximum camera interface input width in pixels + * @max_height: maximum camera interface input height in pixels + * @out_width_align: minimum output width alignment in pixels + * @win_hor_offs_align: minimum camera interface crop window horizontal + * offset alignment in pixels + * @out_hor_offs_align: minimum output DMA compose rectangle horizontal + * offset alignment in pixels + * @max_dma_bufs: number of output DMA buffer start address registers + * @num_instances: total number of FIMC-LITE IP instances available + */ struct flite_drvdata { unsigned short max_width; unsigned short max_height; unsigned short out_width_align; unsigned short win_hor_offs_align; unsigned short out_hor_offs_align; + unsigned short max_dma_bufs; + unsigned short num_instances; }; -#define fimc_lite_get_drvdata(_pdev) \ - ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) - struct fimc_lite_events { unsigned int data_overflow; }; @@ -83,20 +96,22 @@ struct flite_frame { * struct flite_buffer - video buffer structure * @vb: vb2 buffer * @list: list head for the buffers queue - * @paddr: precalculated physical address + * @paddr: DMA buffer start address + * @index: DMA start address register's index */ struct flite_buffer { struct vb2_buffer vb; struct list_head list; dma_addr_t paddr; + unsigned short index; }; /** * struct fimc_lite - fimc lite structure * @pdev: pointer to FIMC-LITE platform device * @dd: SoC specific driver data structure + * @ve: exynos video device entity structure * @v4l2_dev: pointer to top the level v4l2_device - * @vfd: video device node * @fh: v4l2 file handle * @alloc_ctx: videobuf2 memory allocator context * @subdev: FIMC-LITE subdev @@ -122,16 +137,16 @@ struct flite_buffer { * @pending_buf_q: pending buffers queue head * @active_buf_q: the queue head of buffers scheduled in hardware * @vb_queue: vb2 buffers queue + * @buf_index: helps to keep track of the DMA start address register index * @active_buf_count: number of video buffers scheduled in hardware * @frame_count: the captured frames counter * @reqbufs_count: the number of buffers requested with REQBUFS ioctl - * @ref_count: driver's private reference counter */ struct fimc_lite { struct platform_device *pdev; struct flite_drvdata *dd; + struct exynos_video_entity ve; struct v4l2_device *v4l2_dev; - struct video_device vfd; struct v4l2_fh fh; struct vb2_alloc_ctx *alloc_ctx; struct v4l2_subdev subdev; @@ -141,8 +156,6 @@ struct fimc_lite { struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *test_pattern; int index; - struct fimc_pipeline pipeline; - const struct fimc_pipeline_ops *pipeline_ops; struct mutex lock; spinlock_t slock; @@ -161,9 +174,9 @@ struct fimc_lite { struct list_head pending_buf_q; struct list_head active_buf_q; struct vb2_queue vb_queue; + unsigned short buf_index; unsigned int frame_count; unsigned int reqbufs_count; - int ref_count; struct fimc_lite_events events; bool streaming; diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c index bde1f47f7ed..8d33b68c76b 100644 --- a/drivers/media/platform/exynos4-is/fimc-m2m.c +++ b/drivers/media/platform/exynos4-is/fimc-m2m.c @@ -27,6 +27,7 @@ #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> +#include "common.h" #include "fimc-core.h" #include "fimc-reg.h" #include "media-dev.h" diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/exynos4-is/fimc-reg.c index f079f36099d..1db8cb4c46e 100644 --- a/drivers/media/platform/exynos4-is/fimc-reg.c +++ b/drivers/media/platform/exynos4-is/fimc-reg.c @@ -618,7 +618,7 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, } if (i == ARRAY_SIZE(pix_desc)) { - v4l2_err(&vc->vfd, + v4l2_err(&vc->ve.vdev, "Camera color format not supported: %d\n", vc->ci_fmt.code); return -EINVAL; @@ -698,7 +698,7 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; break; default: - v4l2_err(&vid_cap->vfd, + v4l2_err(&vid_cap->ve.vdev, "Not supported camera pixel format: %#x\n", vid_cap->ci_fmt.code); return -EINVAL; @@ -721,7 +721,8 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, WARN_ONCE(1, "ISP Writeback input is not supported\n"); break; default: - v4l2_err(&vid_cap->vfd, "Invalid FIMC bus type selected: %d\n", + v4l2_err(&vid_cap->ve.vdev, + "Invalid FIMC bus type selected: %d\n", source->fimc_bus_type); return -EINVAL; } diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index 15ef8f28239..19f556c5957 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1,8 +1,8 @@ /* * S5P/EXYNOS4 SoC series camera host interface media device driver * - * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki <s.nawrocki@samsung.com> + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -39,6 +39,26 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, struct fimc_source_info *si, bool on); + +/* Set up image sensor subdev -> FIMC capture node notifications. */ +static void __setup_sensor_notification(struct fimc_md *fmd, + struct v4l2_subdev *sensor, + struct v4l2_subdev *fimc_sd) +{ + struct fimc_source_info *src_inf; + struct fimc_sensor_info *md_si; + unsigned long flags; + + src_inf = v4l2_get_subdev_hostdata(sensor); + if (!src_inf || WARN_ON(fmd == NULL)) + return; + + md_si = source_to_sensor_info(src_inf); + spin_lock_irqsave(&fmd->slock, flags); + md_si->host = v4l2_get_subdevdata(fimc_sd); + spin_unlock_irqrestore(&fmd->slock, flags); +} + /** * fimc_pipeline_prepare - update pipeline information with subdevice pointers * @me: media entity terminating the pipeline @@ -46,9 +66,11 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * Caller holds the graph mutex. */ static void fimc_pipeline_prepare(struct fimc_pipeline *p, - struct media_entity *me) + struct media_entity *me) { + struct fimc_md *fmd = entity_to_fimc_mdev(me); struct v4l2_subdev *sd; + struct v4l2_subdev *sensor = NULL; int i; for (i = 0; i < IDX_MAX; i++) @@ -62,7 +84,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_pad *spad = &me->pads[i]; if (!(spad->flags & MEDIA_PAD_FL_SINK)) continue; - pad = media_entity_remote_source(spad); + pad = media_entity_remote_pad(spad); if (pad) break; } @@ -73,8 +95,10 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, sd = media_entity_to_v4l2_subdev(pad->entity); switch (sd->grp_id) { - case GRP_ID_FIMC_IS_SENSOR: case GRP_ID_SENSOR: + sensor = sd; + /* fall through */ + case GRP_ID_FIMC_IS_SENSOR: p->subdevs[IDX_SENSOR] = sd; break; case GRP_ID_CSIS: @@ -84,7 +108,7 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, p->subdevs[IDX_FLITE] = sd; break; case GRP_ID_FIMC: - /* No need to control FIMC subdev through subdev ops */ + p->subdevs[IDX_FIMC] = sd; break; case GRP_ID_FIMC_IS: p->subdevs[IDX_IS_ISP] = sd; @@ -96,6 +120,9 @@ static void fimc_pipeline_prepare(struct fimc_pipeline *p, if (me->num_pads == 1) break; } + + if (sensor && p->subdevs[IDX_FIMC]) + __setup_sensor_notification(fmd, sensor, p->subdevs[IDX_FIMC]); } /** @@ -168,10 +195,11 @@ error: * * Called with the graph mutex held. */ -static int __fimc_pipeline_open(struct fimc_pipeline *p, +static int __fimc_pipeline_open(struct exynos_media_pipeline *ep, struct media_entity *me, bool prepare) { struct fimc_md *fmd = entity_to_fimc_mdev(me); + struct fimc_pipeline *p = to_fimc_pipeline(ep); struct v4l2_subdev *sd; int ret; @@ -214,20 +242,21 @@ err_wbclk: * * Disable power of all subdevs and turn the external sensor clock off. */ -static int __fimc_pipeline_close(struct fimc_pipeline *p) +static int __fimc_pipeline_close(struct exynos_media_pipeline *ep) { + struct fimc_pipeline *p = to_fimc_pipeline(ep); struct v4l2_subdev *sd = p ? p->subdevs[IDX_SENSOR] : NULL; struct fimc_md *fmd; - int ret = 0; - - if (WARN_ON(sd == NULL)) - return -EINVAL; + int ret; - if (p->subdevs[IDX_SENSOR]) { - ret = fimc_pipeline_s_power(p, 0); - fimc_md_set_camclk(sd, false); + if (sd == NULL) { + pr_warn("%s(): No sensor subdev\n", __func__); + return 0; } + ret = fimc_pipeline_s_power(p, 0); + fimc_md_set_camclk(sd, false); + fmd = entity_to_fimc_mdev(&sd->entity); /* Disable PXLASYNC clock if this pipeline includes FIMC-IS */ @@ -242,12 +271,13 @@ static int __fimc_pipeline_close(struct fimc_pipeline *p) * @pipeline: video pipeline structure * @on: passed as the s_stream() callback argument */ -static int __fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) +static int __fimc_pipeline_s_stream(struct exynos_media_pipeline *ep, bool on) { static const u8 seq[2][IDX_MAX] = { { IDX_FIMC, IDX_SENSOR, IDX_IS_ISP, IDX_CSIS, IDX_FLITE }, { IDX_CSIS, IDX_FLITE, IDX_FIMC, IDX_SENSOR, IDX_IS_ISP }, }; + struct fimc_pipeline *p = to_fimc_pipeline(ep); int i, ret = 0; if (p->subdevs[IDX_SENSOR] == NULL) @@ -271,12 +301,38 @@ error: } /* Media pipeline operations for the FIMC/FIMC-LITE video device driver */ -static const struct fimc_pipeline_ops fimc_pipeline_ops = { +static const struct exynos_media_pipeline_ops fimc_pipeline_ops = { .open = __fimc_pipeline_open, .close = __fimc_pipeline_close, .set_stream = __fimc_pipeline_s_stream, }; +static struct exynos_media_pipeline *fimc_md_pipeline_create( + struct fimc_md *fmd) +{ + struct fimc_pipeline *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + + list_add_tail(&p->list, &fmd->pipelines); + + p->ep.ops = &fimc_pipeline_ops; + return &p->ep; +} + +static void fimc_md_pipelines_free(struct fimc_md *fmd) +{ + while (!list_empty(&fmd->pipelines)) { + struct fimc_pipeline *p; + + p = list_entry(fmd->pipelines.next, typeof(*p), list); + list_del(&p->list); + kfree(p); + } +} + /* * Sensor subdevice helper functions */ @@ -592,6 +648,7 @@ static int register_fimc_lite_entity(struct fimc_md *fmd, struct fimc_lite *fimc_lite) { struct v4l2_subdev *sd; + struct exynos_media_pipeline *ep; int ret; if (WARN_ON(fimc_lite->index >= FIMC_LITE_MAX_DEVS || @@ -600,7 +657,12 @@ static int register_fimc_lite_entity(struct fimc_md *fmd, sd = &fimc_lite->subdev; sd->grp_id = GRP_ID_FLITE; - v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); + + ep = fimc_md_pipeline_create(fmd); + if (!ep) + return -ENOMEM; + + v4l2_set_subdev_hostdata(sd, ep); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) @@ -614,6 +676,7 @@ static int register_fimc_lite_entity(struct fimc_md *fmd, static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) { struct v4l2_subdev *sd; + struct exynos_media_pipeline *ep; int ret; if (WARN_ON(fimc->id >= FIMC_MAX_DEVS || fmd->fimc[fimc->id])) @@ -621,7 +684,12 @@ static int register_fimc_entity(struct fimc_md *fmd, struct fimc_dev *fimc) sd = &fimc->vid_cap.subdev; sd->grp_id = GRP_ID_FIMC; - v4l2_set_subdev_hostdata(sd, (void *)&fimc_pipeline_ops); + + ep = fimc_md_pipeline_create(fmd); + if (!ep) + return -ENOMEM; + + v4l2_set_subdev_hostdata(sd, ep); ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (!ret) { @@ -736,8 +804,6 @@ static int fimc_md_pdev_match(struct device *dev, void *data) if (!strcmp(pdev->name, CSIS_DRIVER_NAME)) { plat_entity = IDX_CSIS; - } else if (!strcmp(pdev->name, FIMC_LITE_DRV_NAME)) { - plat_entity = IDX_FLITE; } else { p = strstr(pdev->name, "fimc"); if (p && *(p + 4) == 0) @@ -797,17 +863,19 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) int i; for (i = 0; i < FIMC_MAX_DEVS; i++) { - if (fmd->fimc[i] == NULL) + struct fimc_dev *dev = fmd->fimc[i]; + if (dev == NULL) continue; - v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); - fmd->fimc[i]->pipeline_ops = NULL; + v4l2_device_unregister_subdev(&dev->vid_cap.subdev); + dev->vid_cap.ve.pipe = NULL; fmd->fimc[i] = NULL; } for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { - if (fmd->fimc_lite[i] == NULL) + struct fimc_lite *dev = fmd->fimc_lite[i]; + if (dev == NULL) continue; - v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); - fmd->fimc_lite[i]->pipeline_ops = NULL; + v4l2_device_unregister_subdev(&dev->subdev); + dev->ve.pipe = NULL; fmd->fimc_lite[i] = NULL; } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { @@ -880,18 +948,6 @@ static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]\n", source->name, flags ? '=' : '-', sink->name); - - if (flags == 0 || sensor == NULL) - continue; - - if (!WARN_ON(si == NULL)) { - unsigned long irq_flags; - struct fimc_sensor_info *inf = source_to_sensor_info(si); - - spin_lock_irqsave(&fmd->slock, irq_flags); - inf->host = fmd->fimc[i]; - spin_unlock_irqrestore(&fmd->slock, irq_flags); - } } for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { @@ -929,7 +985,7 @@ static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) continue; source = &fimc->subdev.entity; - sink = &fimc->vfd.entity; + sink = &fimc->ve.vdev.entity; /* FIMC-LITE's subdev and video node */ ret = media_entity_create_link(source, FLITE_SD_PAD_SOURCE_DMA, sink, 0, 0); @@ -1066,7 +1122,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) continue; source = &fmd->fimc[i]->vid_cap.subdev.entity; - sink = &fmd->fimc[i]->vid_cap.vfd.entity; + sink = &fmd->fimc[i]->vid_cap.ve.vdev.entity; ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); @@ -1231,66 +1287,98 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) return __fimc_md_set_camclk(fmd, si, on); } -static int fimc_md_link_notify(struct media_pad *source, - struct media_pad *sink, u32 flags) +static int __fimc_md_modify_pipeline(struct media_entity *entity, bool enable) { - struct fimc_lite *fimc_lite = NULL; - struct fimc_dev *fimc = NULL; - struct fimc_pipeline *pipeline; - struct v4l2_subdev *sd; - struct mutex *lock; - int i, ret = 0; - int ref_count; + struct exynos_video_entity *ve; + struct fimc_pipeline *p; + struct video_device *vdev; + int ret; - if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + vdev = media_entity_to_video_device(entity); + if (vdev->entity.use_count == 0) return 0; - sd = media_entity_to_v4l2_subdev(sink->entity); - - switch (sd->grp_id) { - case GRP_ID_FLITE: - fimc_lite = v4l2_get_subdevdata(sd); - if (WARN_ON(fimc_lite == NULL)) - return 0; - pipeline = &fimc_lite->pipeline; - lock = &fimc_lite->lock; - break; - case GRP_ID_FIMC: - fimc = v4l2_get_subdevdata(sd); - if (WARN_ON(fimc == NULL)) - return 0; - pipeline = &fimc->pipeline; - lock = &fimc->lock; - break; - default: + ve = vdev_to_exynos_video_entity(vdev); + p = to_fimc_pipeline(ve->pipe); + /* + * Nothing to do if we are disabling the pipeline, some link + * has been disconnected and p->subdevs array is cleared now. + */ + if (!enable && p->subdevs[IDX_SENSOR] == NULL) return 0; + + if (enable) + ret = __fimc_pipeline_open(ve->pipe, entity, true); + else + ret = __fimc_pipeline_close(ve->pipe); + + if (ret == 0 && !enable) + memset(p->subdevs, 0, sizeof(p->subdevs)); + + return ret; +} + +/* Locking: called with entity->parent->graph_mutex mutex held. */ +static int __fimc_md_modify_pipelines(struct media_entity *entity, bool enable) +{ + struct media_entity *entity_err = entity; + struct media_entity_graph graph; + int ret; + + /* + * Walk current graph and call the pipeline open/close routine for each + * opened video node that belongs to the graph of entities connected + * through active links. This is needed as we cannot power on/off the + * subdevs in random order. + */ + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + + ret = __fimc_md_modify_pipeline(entity, enable); + + if (ret < 0) + goto err; } - mutex_lock(lock); - ref_count = fimc ? fimc->vid_cap.refcnt : fimc_lite->ref_count; + return 0; + err: + media_entity_graph_walk_start(&graph, entity_err); - if (!(flags & MEDIA_LNK_FL_ENABLED)) { - if (ref_count > 0) { - ret = __fimc_pipeline_close(pipeline); - if (!ret && fimc) - fimc_ctrls_delete(fimc->vid_cap.ctx); - } - for (i = 0; i < IDX_MAX; i++) - pipeline->subdevs[i] = NULL; - } else if (ref_count > 0) { - /* - * Link activation. Enable power of pipeline elements only if - * the pipeline is already in use, i.e. its video node is open. - * Recreate the controls destroyed during the link deactivation. - */ - ret = __fimc_pipeline_open(pipeline, - source->entity, true); - if (!ret && fimc) - ret = fimc_capture_ctrls_create(fimc); + while ((entity_err = media_entity_graph_walk_next(&graph))) { + if (media_entity_type(entity_err) != MEDIA_ENT_T_DEVNODE) + continue; + + __fimc_md_modify_pipeline(entity_err, !enable); + + if (entity_err == entity) + break; + } + + return ret; +} + +static int fimc_md_link_notify(struct media_link *link, unsigned int flags, + unsigned int notification) +{ + struct media_entity *sink = link->sink->entity; + int ret = 0; + + /* Before link disconnection */ + if (notification == MEDIA_DEV_NOTIFY_PRE_LINK_CH) { + if (!(flags & MEDIA_LNK_FL_ENABLED)) + ret = __fimc_md_modify_pipelines(sink, false); + else + ; /* TODO: Link state change validation */ + /* After link activation */ + } else if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (link->flags & MEDIA_LNK_FL_ENABLED)) { + ret = __fimc_md_modify_pipelines(sink, true); } - mutex_unlock(lock); - return ret ? -EPIPE : ret; + return ret ? -EPIPE : 0; } static ssize_t fimc_md_sysfs_show(struct device *dev, @@ -1370,6 +1458,7 @@ static int fimc_md_probe(struct platform_device *pdev) spin_lock_init(&fmd->slock); fmd->pdev = pdev; + INIT_LIST_HEAD(&fmd->pipelines); strlcpy(fmd->media_dev.model, "SAMSUNG S5P FIMC", sizeof(fmd->media_dev.model)); @@ -1457,6 +1546,7 @@ static int fimc_md_remove(struct platform_device *pdev) return 0; device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode); fimc_md_unregister_entities(fmd); + fimc_md_pipelines_free(fmd); media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); return 0; diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/exynos4-is/media-dev.h index 44d86b61d66..62599fd7756 100644 --- a/drivers/media/platform/exynos4-is/media-dev.h +++ b/drivers/media/platform/exynos4-is/media-dev.h @@ -18,6 +18,7 @@ #include <media/media-entity.h> #include <media/v4l2-device.h> #include <media/v4l2-subdev.h> +#include <media/s5p_fimc.h> #include "fimc-core.h" #include "fimc-lite.h" @@ -40,6 +41,29 @@ enum { FIMC_MAX_WBCLKS }; +enum fimc_subdev_index { + IDX_SENSOR, + IDX_CSIS, + IDX_FLITE, + IDX_IS_ISP, + IDX_FIMC, + IDX_MAX, +}; + +/* + * This structure represents a chain of media entities, including a data + * source entity (e.g. an image sensor subdevice), a data capture entity + * - a video capture device node and any remaining entities. + */ +struct fimc_pipeline { + struct exynos_media_pipeline ep; + struct list_head list; + struct media_entity *vdev_entity; + struct v4l2_subdev *subdevs[IDX_MAX]; +}; + +#define to_fimc_pipeline(_ep) container_of(_ep, struct fimc_pipeline, ep) + struct fimc_csis_info { struct v4l2_subdev *sd; int id; @@ -104,17 +128,11 @@ struct fimc_md { struct pinctrl_state *state_idle; } pinctl; bool user_subdev_api; + spinlock_t slock; + struct list_head pipelines; }; -#define is_subdev_pad(pad) (pad == NULL || \ - media_entity_type(pad->entity) == MEDIA_ENT_T_V4L2_SUBDEV) - -#define me_subtype(me) \ - ((me->type) & (MEDIA_ENT_TYPE_MASK | MEDIA_ENT_SUBTYPE_MASK)) - -#define subdev_has_devnode(__sd) (__sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) - static inline struct fimc_sensor_info *source_to_sensor_info(struct fimc_source_info *si) { @@ -127,14 +145,14 @@ static inline struct fimc_md *entity_to_fimc_mdev(struct media_entity *me) container_of(me->parent, struct fimc_md, media_dev); } -static inline void fimc_md_graph_lock(struct fimc_dev *fimc) +static inline void fimc_md_graph_lock(struct exynos_video_entity *ve) { - mutex_lock(&fimc->vid_cap.vfd.entity.parent->graph_mutex); + mutex_lock(&ve->vdev.entity.parent->graph_mutex); } -static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) +static inline void fimc_md_graph_unlock(struct exynos_video_entity *ve) { - mutex_unlock(&fimc->vid_cap.vfd.entity.parent->graph_mutex); + mutex_unlock(&ve->vdev.entity.parent->graph_mutex); } int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); @@ -149,4 +167,16 @@ static inline bool fimc_md_is_isp_available(struct device_node *node) #define fimc_md_is_isp_available(node) (false) #endif /* CONFIG_OF */ +static inline struct v4l2_subdev *__fimc_md_get_subdev( + struct exynos_media_pipeline *ep, + unsigned int index) +{ + struct fimc_pipeline *p = to_fimc_pipeline(ep); + + if (!p || index >= IDX_MAX) + return NULL; + else + return p->subdevs[index]; +} + #endif diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/exynos4-is/mipi-csis.c index 254d70fe762..0914230b42d 100644 --- a/drivers/media/platform/exynos4-is/mipi-csis.c +++ b/drivers/media/platform/exynos4-is/mipi-csis.c @@ -1,8 +1,8 @@ /* - * Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver + * Samsung S5P/EXYNOS SoC series MIPI-CSI receiver driver * - * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. - * Sylwester Nawrocki <s.nawrocki@samsung.com> + * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd. + * Author: Sylwester Nawrocki <s.nawrocki@samsung.com> * * 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 @@ -66,11 +66,12 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); /* Interrupt mask */ #define S5PCSIS_INTMSK 0x10 -#define S5PCSIS_INTMSK_EN_ALL 0xf000103f #define S5PCSIS_INTMSK_EVEN_BEFORE (1 << 31) #define S5PCSIS_INTMSK_EVEN_AFTER (1 << 30) #define S5PCSIS_INTMSK_ODD_BEFORE (1 << 29) #define S5PCSIS_INTMSK_ODD_AFTER (1 << 28) +#define S5PCSIS_INTMSK_FRAME_START (1 << 27) +#define S5PCSIS_INTMSK_FRAME_END (1 << 26) #define S5PCSIS_INTMSK_ERR_SOT_HS (1 << 12) #define S5PCSIS_INTMSK_ERR_LOST_FS (1 << 5) #define S5PCSIS_INTMSK_ERR_LOST_FE (1 << 4) @@ -78,6 +79,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define S5PCSIS_INTMSK_ERR_ECC (1 << 2) #define S5PCSIS_INTMSK_ERR_CRC (1 << 1) #define S5PCSIS_INTMSK_ERR_UNKNOWN (1 << 0) +#define S5PCSIS_INTMSK_EXYNOS4_EN_ALL 0xf000103f +#define S5PCSIS_INTMSK_EXYNOS5_EN_ALL 0xfc00103f /* Interrupt source */ #define S5PCSIS_INTSRC 0x14 @@ -88,6 +91,8 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define S5PCSIS_INTSRC_ODD_AFTER (1 << 28) #define S5PCSIS_INTSRC_ODD (0x3 << 28) #define S5PCSIS_INTSRC_NON_IMAGE_DATA (0xff << 28) +#define S5PCSIS_INTSRC_FRAME_START (1 << 27) +#define S5PCSIS_INTSRC_FRAME_END (1 << 26) #define S5PCSIS_INTSRC_ERR_SOT_HS (0xf << 12) #define S5PCSIS_INTSRC_ERR_LOST_FS (1 << 5) #define S5PCSIS_INTSRC_ERR_LOST_FE (1 << 4) @@ -151,6 +156,9 @@ static const struct s5pcsis_event s5pcsis_events[] = { { S5PCSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" }, { S5PCSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" }, { S5PCSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" }, + /* Frame start/end */ + { S5PCSIS_INTSRC_FRAME_START, "Frame Start" }, + { S5PCSIS_INTSRC_FRAME_END, "Frame End" }, }; #define S5PCSIS_NUM_EVENTS ARRAY_SIZE(s5pcsis_events) @@ -159,6 +167,11 @@ struct csis_pktbuf { unsigned int len; }; +struct csis_drvdata { + /* Mask of all used interrupts in S5PCSIS_INTMSK register */ + u32 interrupt_mask; +}; + /** * struct csis_state - the driver's internal state data structure * @lock: mutex serializing the subdev and power management operations, @@ -171,6 +184,7 @@ struct csis_pktbuf { * @supplies: CSIS regulator supplies * @clock: CSIS clocks * @irq: requested s5p-mipi-csis irq number + * @interrupt_mask: interrupt mask of the all used interrupts * @flags: the state variable for power and streaming control * @clock_frequency: device bus clock frequency * @hs_settle: HS-RX settle time @@ -193,6 +207,7 @@ struct csis_state { struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES]; struct clk *clock[NUM_CSIS_CLOCKS]; int irq; + u32 interrupt_mask; u32 flags; u32 clk_frequency; @@ -274,9 +289,10 @@ static const struct csis_pix_format *find_csis_format( static void s5pcsis_enable_interrupts(struct csis_state *state, bool on) { u32 val = s5pcsis_read(state, S5PCSIS_INTMSK); - - val = on ? val | S5PCSIS_INTMSK_EN_ALL : - val & ~S5PCSIS_INTMSK_EN_ALL; + if (on) + val |= state->interrupt_mask; + else + val &= ~state->interrupt_mask; s5pcsis_write(state, S5PCSIS_INTMSK, val); } @@ -771,8 +787,12 @@ static int s5pcsis_parse_dt(struct platform_device *pdev, #define s5pcsis_parse_dt(pdev, state) (-ENOSYS) #endif +static const struct of_device_id s5pcsis_of_match[]; + static int s5pcsis_probe(struct platform_device *pdev) { + const struct of_device_id *of_id; + const struct csis_drvdata *drv_data; struct device *dev = &pdev->dev; struct resource *mem_res; struct csis_state *state; @@ -787,10 +807,19 @@ static int s5pcsis_probe(struct platform_device *pdev) spin_lock_init(&state->slock); state->pdev = pdev; - if (dev->of_node) + if (dev->of_node) { + of_id = of_match_node(s5pcsis_of_match, dev->of_node); + if (WARN_ON(of_id == NULL)) + return -EINVAL; + + drv_data = of_id->data; + state->interrupt_mask = drv_data->interrupt_mask; + ret = s5pcsis_parse_dt(pdev, state); - else + } else { ret = s5pcsis_get_platform_data(pdev, state); + } + if (ret < 0) return ret; @@ -994,9 +1023,25 @@ static const struct dev_pm_ops s5pcsis_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(s5pcsis_suspend, s5pcsis_resume) }; +static const struct csis_drvdata exynos4_csis_drvdata = { + .interrupt_mask = S5PCSIS_INTMSK_EXYNOS4_EN_ALL, +}; + +static const struct csis_drvdata exynos5_csis_drvdata = { + .interrupt_mask = S5PCSIS_INTMSK_EXYNOS5_EN_ALL, +}; + static const struct of_device_id s5pcsis_of_match[] = { - { .compatible = "samsung,s5pv210-csis" }, - { .compatible = "samsung,exynos4210-csis" }, + { + .compatible = "samsung,s5pv210-csis", + .data = &exynos4_csis_drvdata, + }, { + .compatible = "samsung,exynos4210-csis", + .data = &exynos4_csis_drvdata, + }, { + .compatible = "samsung,exynos5250-csis", + .data = &exynos5_csis_drvdata, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, s5pcsis_of_match); diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c index 3a6a0dcdc3e..221ec428a01 100644 --- a/drivers/media/platform/fsl-viu.c +++ b/drivers/media/platform/fsl-viu.c @@ -1475,7 +1475,6 @@ static struct video_device viu_template = { .release = video_device_release, .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL, - .current_norm = V4L2_STD_NTSC_M, }; static int viu_of_probe(struct platform_device *op) @@ -1546,6 +1545,7 @@ static int viu_of_probe(struct platform_device *op) viu_dev->vidq.timeout.function = viu_vid_timeout; viu_dev->vidq.timeout.data = (unsigned long)viu_dev; init_timer(&viu_dev->vidq.timeout); + viu_dev->std = V4L2_STD_NTSC_M; viu_dev->first = 1; /* Allocate memory for video device */ diff --git a/drivers/media/platform/indycam.c b/drivers/media/platform/indycam.c index 548236333cc..f1d192bbcb4 100644 --- a/drivers/media/platform/indycam.c +++ b/drivers/media/platform/indycam.c @@ -23,7 +23,6 @@ #include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-device.h> -#include <media/v4l2-chip-ident.h> #include "indycam.h" @@ -283,20 +282,9 @@ static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) /* I2C-interface */ -static int indycam_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct indycam *camera = to_indycam(sd); - - return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM, - camera->version); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops indycam_core_ops = { - .g_chip_ident = indycam_g_chip_ident, .g_ctrl = indycam_g_ctrl, .s_ctrl = indycam_s_ctrl, }; diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c index 75856464958..540516ca872 100644 --- a/drivers/media/platform/m2m-deinterlace.c +++ b/drivers/media/platform/m2m-deinterlace.c @@ -1033,6 +1033,7 @@ static int deinterlace_probe(struct platform_device *pdev) *vfd = deinterlace_videodev; vfd->lock = &pcdev->dev_mutex; + vfd->v4l2_dev = &pcdev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c index d030f9beae8..1f079ff33d4 100644 --- a/drivers/media/platform/marvell-ccic/cafe-driver.c +++ b/drivers/media/platform/marvell-ccic/cafe-driver.c @@ -27,7 +27,6 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-chip-ident.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/delay.h> @@ -469,7 +468,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, goto out; cam->pdev = pdev; mcam = &cam->mcam; - mcam->chip_id = V4L2_IDENT_CAFE; + mcam->chip_id = MCAM_CAFE; spin_lock_init(&mcam->dev_lock); init_waitqueue_head(&cam->smbus_wait); mcam->plat_power_up = cafe_ctlr_power_up; @@ -501,6 +500,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); goto out_disable; } + mcam->regs_size = pci_resource_len(pdev, 0); ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); if (ret) goto out_iounmap; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 64ab91edfb8..0821ed08c12 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -23,7 +23,6 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-ctrls.h> -#include <media/v4l2-chip-ident.h> #include <media/ov7670.h> #include <media/videobuf2-vmalloc.h> #include <media/videobuf2-dma-contig.h> @@ -336,7 +335,7 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam) mcam_reg_clear_bit(cam, REG_CTRL1, C1_TWOBUFS); } else mcam_reg_set_bit(cam, REG_CTRL1, C1_TWOBUFS); - if (cam->chip_id == V4L2_IDENT_CAFE) + if (cam->chip_id == MCAM_CAFE) mcam_reg_write(cam, REG_UBAR, 0); /* 32 bits only */ } @@ -796,7 +795,6 @@ static int __mcam_cam_reset(struct mcam_camera *cam) */ static int mcam_cam_init(struct mcam_camera *cam) { - struct v4l2_dbg_chip_ident chip; int ret; mutex_lock(&cam->s_mutex); @@ -804,24 +802,8 @@ static int mcam_cam_init(struct mcam_camera *cam) cam_warn(cam, "Cam init with device in funky state %d", cam->state); ret = __mcam_cam_reset(cam); - if (ret) - goto out; - chip.ident = V4L2_IDENT_NONE; - chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; - chip.match.addr = cam->sensor_addr; - ret = sensor_call(cam, core, g_chip_ident, &chip); - if (ret) - goto out; - cam->sensor_type = chip.ident; - if (cam->sensor_type != V4L2_IDENT_OV7670) { - cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type); - ret = -EINVAL; - goto out; - } -/* Get/set parameters? */ - ret = 0; + /* Get/set parameters? */ cam->state = S_IDLE; -out: mcam_ctlr_power_down(cam); mutex_unlock(&cam->s_mutex); return ret; @@ -1362,6 +1344,12 @@ static int mcam_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id a) return 0; } +static int mcam_vidioc_g_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + *a = V4L2_STD_NTSC_M; + return 0; +} + /* * G/S_PARM. Most of this is done by the sensor, but we are * the level which controls the number of read buffers. @@ -1392,20 +1380,6 @@ static int mcam_vidioc_s_parm(struct file *filp, void *priv, return ret; } -static int mcam_vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct mcam_camera *cam = priv; - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - if (v4l2_chip_match_host(&chip->match)) { - chip->ident = cam->chip_id; - return 0; - } - return sensor_call(cam, core, g_chip_ident, chip); -} - static int mcam_vidioc_enum_framesizes(struct file *filp, void *priv, struct v4l2_frmsizeenum *sizes) { @@ -1436,12 +1410,11 @@ static int mcam_vidioc_g_register(struct file *file, void *priv, { struct mcam_camera *cam = priv; - if (v4l2_chip_match_host(®->match)) { - reg->val = mcam_reg_read(cam, reg->reg); - reg->size = 4; - return 0; - } - return sensor_call(cam, core, g_register, reg); + if (reg->reg > cam->regs_size - 4) + return -EINVAL; + reg->val = mcam_reg_read(cam, reg->reg); + reg->size = 4; + return 0; } static int mcam_vidioc_s_register(struct file *file, void *priv, @@ -1449,11 +1422,10 @@ static int mcam_vidioc_s_register(struct file *file, void *priv, { struct mcam_camera *cam = priv; - if (v4l2_chip_match_host(®->match)) { - mcam_reg_write(cam, reg->reg, reg->val); - return 0; - } - return sensor_call(cam, core, s_register, reg); + if (reg->reg > cam->regs_size - 4) + return -EINVAL; + mcam_reg_write(cam, reg->reg, reg->val); + return 0; } #endif @@ -1467,6 +1439,7 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { .vidioc_g_input = mcam_vidioc_g_input, .vidioc_s_input = mcam_vidioc_s_input, .vidioc_s_std = mcam_vidioc_s_std, + .vidioc_g_std = mcam_vidioc_g_std, .vidioc_reqbufs = mcam_vidioc_reqbufs, .vidioc_querybuf = mcam_vidioc_querybuf, .vidioc_qbuf = mcam_vidioc_qbuf, @@ -1477,7 +1450,6 @@ static const struct v4l2_ioctl_ops mcam_v4l_ioctl_ops = { .vidioc_s_parm = mcam_vidioc_s_parm, .vidioc_enum_framesizes = mcam_vidioc_enum_framesizes, .vidioc_enum_frameintervals = mcam_vidioc_enum_frameintervals, - .vidioc_g_chip_ident = mcam_vidioc_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = mcam_vidioc_g_register, .vidioc_s_register = mcam_vidioc_s_register, @@ -1593,7 +1565,6 @@ static const struct v4l2_file_operations mcam_v4l_fops = { static struct video_device mcam_v4l_template = { .name = "mcam", .tvnorms = V4L2_STD_NTSC_M, - .current_norm = V4L2_STD_NTSC_M, /* make mplayer happy */ .fops = &mcam_v4l_fops, .ioctl_ops = &mcam_v4l_ioctl_ops, @@ -1695,7 +1666,7 @@ int mccic_register(struct mcam_camera *cam) if (buffer_mode >= 0) cam->buffer_mode = buffer_mode; if (cam->buffer_mode == B_DMA_sg && - cam->chip_id == V4L2_IDENT_CAFE) { + cam->chip_id == MCAM_CAFE) { printk(KERN_ERR "marvell-cam: Cafe can't do S/G I/O, " "attempting vmalloc mode instead\n"); cam->buffer_mode = B_vmalloc; diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h index 01dec9e5fc2..520c8ded944 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.h +++ b/drivers/media/platform/marvell-ccic/mcam-core.h @@ -53,6 +53,11 @@ enum mcam_buffer_mode { B_DMA_sg = 2 }; +enum mcam_chip_id { + MCAM_CAFE, + MCAM_ARMADA610, +}; + /* * Is a given buffer mode supported by the current kernel configuration? */ @@ -96,9 +101,10 @@ struct mcam_camera { */ struct i2c_adapter *i2c_adapter; unsigned char __iomem *regs; + unsigned regs_size; /* size in bytes of the register space */ spinlock_t dev_lock; struct device *dev; /* For messages, dma alloc */ - unsigned int chip_id; + enum mcam_chip_id chip_id; short int clock_speed; /* Sensor clock speed, default 30 */ short int use_smbus; /* SMBUS or straight I2c? */ enum mcam_buffer_mode buffer_mode; @@ -152,7 +158,6 @@ struct mcam_camera { void (*frame_complete)(struct mcam_camera *cam, int frame); /* Current operating parameters */ - u32 sensor_type; /* Currently ov7670 only */ struct v4l2_pix_format pix_format; enum v4l2_mbus_pixelcode mbus_code; diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c index c4c17fe76c0..a634888271c 100644 --- a/drivers/media/platform/marvell-ccic/mmp-driver.c +++ b/drivers/media/platform/marvell-ccic/mmp-driver.c @@ -18,7 +18,6 @@ #include <linux/slab.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-chip-ident.h> #include <media/mmp-camera.h> #include <linux/device.h> #include <linux/platform_device.h> @@ -185,7 +184,7 @@ static int mmpcam_probe(struct platform_device *pdev) mcam->plat_power_down = mmpcam_power_down; mcam->dev = &pdev->dev; mcam->use_smbus = 0; - mcam->chip_id = V4L2_IDENT_ARMADA610; + mcam->chip_id = MCAM_ARMADA610; mcam->buffer_mode = B_DMA_sg; spin_lock_init(&mcam->dev_lock); /* @@ -203,6 +202,7 @@ static int mmpcam_probe(struct platform_device *pdev) ret = -ENODEV; goto out_free; } + mcam->regs_size = resource_size(res); /* * Power/clock memory is elsewhere; get it too. Perhaps this * should really be managed outside of this driver? diff --git a/drivers/media/platform/mem2mem_testdev.c b/drivers/media/platform/mem2mem_testdev.c index 4cc7f65d7d7..6a17676f9d7 100644 --- a/drivers/media/platform/mem2mem_testdev.c +++ b/drivers/media/platform/mem2mem_testdev.c @@ -1051,6 +1051,7 @@ static int m2mtest_probe(struct platform_device *pdev) *vfd = m2mtest_videodev; vfd->lock = &dev->dev_mutex; + vfd->v4l2_dev = &dev->v4l2_dev; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { @@ -1061,7 +1062,7 @@ static int m2mtest_probe(struct platform_device *pdev) video_set_drvdata(vfd, dev); snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name); dev->vfd = vfd; - v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME + v4l2_info(&dev->v4l2_dev, "Device registered as /dev/video%d\n", vfd->num); setup_timer(&dev->timer, device_isr, (long)dev); diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/mx2_emmaprp.c index f7440e585b6..c690435853b 100644 --- a/drivers/media/platform/mx2_emmaprp.c +++ b/drivers/media/platform/mx2_emmaprp.c @@ -937,6 +937,7 @@ static int emmaprp_probe(struct platform_device *pdev) *vfd = emmaprp_videodev; vfd->lock = &pcdev->dev_mutex; + vfd->v4l2_dev = &pcdev->v4l2_dev; video_set_drvdata(vfd, pcdev); snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name); diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c index d338b19da54..dfd0a21a065 100644 --- a/drivers/media/platform/omap/omap_vout.c +++ b/drivers/media/platform/omap/omap_vout.c @@ -335,8 +335,6 @@ static int video_mode_to_dss_mode(struct omap_vout_device *vout) ovl = ovid->overlays[0]; switch (pix->pixelformat) { - case 0: - break; case V4L2_PIX_FMT_YUYV: mode = OMAP_DSS_COLOR_YUV2; break; @@ -358,6 +356,7 @@ static int video_mode_to_dss_mode(struct omap_vout_device *vout) break; default: mode = -EINVAL; + break; } return mode; } diff --git a/drivers/media/platform/omap24xxcam.c b/drivers/media/platform/omap24xxcam.c index debb44ceb18..d2b440c842b 100644 --- a/drivers/media/platform/omap24xxcam.c +++ b/drivers/media/platform/omap24xxcam.c @@ -1656,7 +1656,7 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s) } vfd->release = video_device_release; - vfd->parent = cam->dev; + vfd->v4l2_dev = &cam->v4l2_dev; strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); vfd->fops = &omap24xxcam_fops; @@ -1752,6 +1752,11 @@ static int omap24xxcam_probe(struct platform_device *pdev) cam->dev = &pdev->dev; + if (v4l2_device_register(&pdev->dev, &cam->v4l2_dev)) { + dev_err(&pdev->dev, "v4l2_device_register failed\n"); + goto err; + } + /* * Impose a lower limit on the amount of memory allocated for * capture. We require at least enough memory to double-buffer @@ -1849,6 +1854,8 @@ static int omap24xxcam_remove(struct platform_device *pdev) cam->mmio_base_phys = 0; } + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); return 0; diff --git a/drivers/media/platform/omap24xxcam.h b/drivers/media/platform/omap24xxcam.h index c4395956a49..7f6f7915553 100644 --- a/drivers/media/platform/omap24xxcam.h +++ b/drivers/media/platform/omap24xxcam.h @@ -29,6 +29,7 @@ #include <media/videobuf-dma-sg.h> #include <media/v4l2-int-device.h> +#include <media/v4l2-device.h> /* * @@ -462,6 +463,8 @@ struct omap24xxcam_device { */ struct mutex mutex; + struct v4l2_device v4l2_dev; + /*** general driver state information ***/ atomic_t users; /* diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 1d7dbd5c0fb..df3a0ec7fd2 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -792,9 +792,9 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) /* * isp_pipeline_link_notify - Link management notification callback - * @source: Pad at the start of the link - * @sink: Pad at the end of the link + * @link: The link * @flags: New link flags that will be applied + * @notification: The link's state change notification type (MEDIA_DEV_NOTIFY_*) * * React to link management on powered pipelines by updating the use count of * all entities in the source and sink sides of the link. Entities are powered @@ -804,29 +804,38 @@ int omap3isp_pipeline_pm_use(struct media_entity *entity, int use) * off is assumed to never fail. This function will not fail for disconnection * events. */ -static int isp_pipeline_link_notify(struct media_pad *source, - struct media_pad *sink, u32 flags) +static int isp_pipeline_link_notify(struct media_link *link, u32 flags, + unsigned int notification) { - int source_use = isp_pipeline_pm_use_count(source->entity); - int sink_use = isp_pipeline_pm_use_count(sink->entity); + struct media_entity *source = link->source->entity; + struct media_entity *sink = link->sink->entity; + int source_use = isp_pipeline_pm_use_count(source); + int sink_use = isp_pipeline_pm_use_count(sink); int ret; - if (!(flags & MEDIA_LNK_FL_ENABLED)) { + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + !(link->flags & MEDIA_LNK_FL_ENABLED)) { /* Powering off entities is assumed to never fail. */ - isp_pipeline_pm_power(source->entity, -sink_use); - isp_pipeline_pm_power(sink->entity, -source_use); + isp_pipeline_pm_power(source, -sink_use); + isp_pipeline_pm_power(sink, -source_use); return 0; } - ret = isp_pipeline_pm_power(source->entity, sink_use); - if (ret < 0) - return ret; + if (notification == MEDIA_DEV_NOTIFY_POST_LINK_CH && + (flags & MEDIA_LNK_FL_ENABLED)) { - ret = isp_pipeline_pm_power(sink->entity, source_use); - if (ret < 0) - isp_pipeline_pm_power(source->entity, -sink_use); + ret = isp_pipeline_pm_power(source, sink_use); + if (ret < 0) + return ret; - return ret; + ret = isp_pipeline_pm_power(sink, source_use); + if (ret < 0) + isp_pipeline_pm_power(source, -sink_use); + + return ret; + } + + return 0; } /* ----------------------------------------------------------------------------- @@ -877,7 +886,7 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_source(pad); + pad = media_entity_remote_pad(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; @@ -967,7 +976,7 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - pad = media_entity_remote_source(pad); + pad = media_entity_remote_pad(pad); if (pad == NULL || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) break; @@ -1083,7 +1092,7 @@ static int isp_pipeline_is_last(struct media_entity *me) pipe = to_isp_pipeline(me); if (pipe->stream_state == ISP_PIPELINE_STREAM_STOPPED) return 0; - pad = media_entity_remote_source(&pipe->output->pad); + pad = media_entity_remote_pad(&pipe->output->pad); return pad->entity == me; } @@ -2249,6 +2258,7 @@ static int isp_probe(struct platform_device *pdev) ret = iommu_attach_device(isp->domain, &pdev->dev); if (ret) { dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); + ret = -EPROBE_DEFER; goto free_domain; } @@ -2287,12 +2297,11 @@ detach_dev: iommu_detach_device(isp->domain, &pdev->dev); free_domain: iommu_domain_free(isp->domain); + isp->domain = NULL; error_isp: isp_xclk_cleanup(isp); omap3isp_put(isp); error: - platform_set_drvdata(pdev, NULL); - mutex_destroy(&isp->isp_mutex); return ret; diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 60e60aa64fb..907a205da5a 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1120,7 +1120,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) u32 syn_mode; u32 ccdc_pattern; - pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); + pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); if (ccdc->input == CCDC_INPUT_PARALLEL) pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/omap3isp/ispccp2.c index c5d84c977e2..e71651429dd 100644 --- a/drivers/media/platform/omap3isp/ispccp2.c +++ b/drivers/media/platform/omap3isp/ispccp2.c @@ -158,13 +158,17 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) * @ccp2: pointer to ISP CCP2 device * @enable: enable/disable flag */ -static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) +static int ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) { struct isp_device *isp = to_isp_device(ccp2); + int ret; int i; - if (enable && ccp2->vdds_csib) - regulator_enable(ccp2->vdds_csib); + if (enable && ccp2->vdds_csib) { + ret = regulator_enable(ccp2->vdds_csib); + if (ret < 0) + return ret; + } /* Enable/Disable all the LCx channels */ for (i = 0; i < CCP2_LCx_CHANS_NUM; i++) @@ -179,6 +183,8 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) if (!enable && ccp2->vdds_csib) regulator_disable(ccp2->vdds_csib); + + return 0; } /* @@ -360,7 +366,7 @@ static int ccp2_if_configure(struct isp_ccp2_device *ccp2) ccp2_pwr_cfg(ccp2); - pad = media_entity_remote_source(&ccp2->pads[CCP2_PAD_SINK]); + pad = media_entity_remote_pad(&ccp2->pads[CCP2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); pdata = sensor->host_priv; @@ -851,7 +857,12 @@ static int ccp2_s_stream(struct v4l2_subdev *sd, int enable) ccp2_print_status(ccp2); /* Enable CSI1/CCP2 interface */ - ccp2_if_enable(ccp2, 1); + ret = ccp2_if_enable(ccp2, 1); + if (ret < 0) { + if (ccp2->phy) + omap3isp_csiphy_release(ccp2->phy); + return ret; + } break; case ISP_PIPELINE_STREAM_SINGLESHOT: diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/omap3isp/ispcsi2.c index 783f4b05b15..6db245d84bb 100644 --- a/drivers/media/platform/omap3isp/ispcsi2.c +++ b/drivers/media/platform/omap3isp/ispcsi2.c @@ -573,7 +573,7 @@ static int csi2_configure(struct isp_csi2_device *csi2) if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) return -EBUSY; - pad = media_entity_remote_source(&csi2->pads[CSI2_PAD_SINK]); + pad = media_entity_remote_pad(&csi2->pads[CSI2_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); pdata = sensor->host_priv; diff --git a/drivers/media/platform/omap3isp/ispqueue.h b/drivers/media/platform/omap3isp/ispqueue.h index 908dfd712e8..3e048ad6564 100644 --- a/drivers/media/platform/omap3isp/ispqueue.h +++ b/drivers/media/platform/omap3isp/ispqueue.h @@ -28,6 +28,7 @@ #include <linux/kernel.h> #include <linux/list.h> +#include <linux/mm_types.h> #include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/wait.h> diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 8dac17511e6..a908d006f52 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -219,7 +219,7 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) { struct media_pad *remote; - remote = media_entity_remote_source(&video->pad); + remote = media_entity_remote_pad(&video->pad); if (remote == NULL || media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV) @@ -314,7 +314,7 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) * entity can be found, and stop checking the pipeline if the * source entity isn't a subdev. */ - pad = media_entity_remote_source(pad); + pad = media_entity_remote_pad(pad); if (pad == NULL) return -EPIPE; @@ -901,7 +901,7 @@ static int isp_video_check_external_subdevs(struct isp_video *video, continue; /* ISP entities have always sink pad == 0. Find source. */ - source_pad = media_entity_remote_source(&ents[i]->pads[0]); + source_pad = media_entity_remote_pad(&ents[i]->pads[0]); if (source_pad == NULL) continue; diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index 70438a0f62a..40b298ab87f 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -845,7 +845,7 @@ static int camif_pipeline_validate(struct camif_dev *camif) int ret; /* Retrieve format at the sensor subdev source pad */ - pad = media_entity_remote_source(&camif->pads[0]); + pad = media_entity_remote_pad(&camif->pads[0]); if (!pad || media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return -EPIPE; diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/s3c-camif/camif-core.c index 0d0fab1a7b5..b38574702fe 100644 --- a/drivers/media/platform/s3c-camif/camif-core.c +++ b/drivers/media/platform/s3c-camif/camif-core.c @@ -341,10 +341,11 @@ static void camif_clk_put(struct camif_dev *camif) int i; for (i = 0; i < CLK_MAX_NUM; i++) { - if (IS_ERR_OR_NULL(camif->clock[i])) + if (IS_ERR(camif->clock[i])) continue; clk_unprepare(camif->clock[i]); clk_put(camif->clock[i]); + camif->clock[i] = ERR_PTR(-EINVAL); } } @@ -352,6 +353,9 @@ static int camif_clk_get(struct camif_dev *camif) { int ret, i; + for (i = 1; i < CLK_MAX_NUM; i++) + camif->clock[i] = ERR_PTR(-EINVAL); + for (i = 0; i < CLK_MAX_NUM; i++) { camif->clock[i] = clk_get(camif->dev, camif_clocks[i]); if (IS_ERR(camif->clock[i])) { diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c index 1a3b4fc05ec..a9e3b16460b 100644 --- a/drivers/media/platform/s3c-camif/camif-regs.c +++ b/drivers/media/platform/s3c-camif/camif-regs.c @@ -379,7 +379,7 @@ static void camif_hw_set_prescaler(struct camif_vp *vp) camif_write(camif, S3C_CAMIF_REG_CISCPREDST(vp->id, vp->offset), cfg); } -void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) +static void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) { struct camif_dev *camif = vp->camif; struct camif_scaler *scaler = &vp->scaler; @@ -426,7 +426,7 @@ void camif_s3c244x_hw_set_scaler(struct camif_vp *vp) scaler->main_h_ratio, scaler->main_v_ratio); } -void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp) +static void camif_s3c64xx_hw_set_scaler(struct camif_vp *vp) { struct camif_dev *camif = vp->camif; struct camif_scaler *scaler = &vp->scaler; @@ -601,6 +601,6 @@ void camif_hw_dump_regs(struct camif_dev *camif, const char *label) pr_info("--- %s ---\n", label); for (i = 0; i < ARRAY_SIZE(registers); i++) { u32 cfg = readl(camif->io_base + registers[i].offset); - printk(KERN_INFO "%s:\t0x%08x\n", registers[i].name, cfg); + dev_info(camif->dev, "%s:\t0x%08x\n", registers[i].name, cfg); } } diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index d12faa691af..a130dcdb720 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -1424,7 +1424,7 @@ static void *mfc_get_drv_data(struct platform_device *pdev) if (pdev->dev.of_node) { const struct of_device_id *match; - match = of_match_node(of_match_ptr(exynos_mfc_match), + match = of_match_node(exynos_mfc_match, pdev->dev.of_node); if (match) driver_data = (struct s5p_mfc_variant *)match->data; diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c index 4e86626dad4..1b34c362985 100644 --- a/drivers/media/platform/s5p-tv/hdmi_drv.c +++ b/drivers/media/platform/s5p-tv/hdmi_drv.c @@ -576,16 +576,22 @@ static int hdmi_s_stream(struct v4l2_subdev *sd, int enable) return hdmi_streamoff(hdev); } -static void hdmi_resource_poweron(struct hdmi_resources *res) +static int hdmi_resource_poweron(struct hdmi_resources *res) { + int ret; + /* turn HDMI power on */ - regulator_bulk_enable(res->regul_count, res->regul_bulk); + ret = regulator_bulk_enable(res->regul_count, res->regul_bulk); + if (ret < 0) + return ret; /* power-on hdmi physical interface */ clk_enable(res->hdmiphy); /* use VPP as parent clock; HDMIPHY is not working yet */ clk_set_parent(res->sclk_hdmi, res->sclk_pixel); /* turn clocks on */ clk_enable(res->sclk_hdmi); + + return 0; } static void hdmi_resource_poweroff(struct hdmi_resources *res) @@ -728,11 +734,13 @@ static int hdmi_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - int ret = 0; + int ret; dev_dbg(dev, "%s\n", __func__); - hdmi_resource_poweron(&hdev->res); + ret = hdmi_resource_poweron(&hdev->res); + if (ret < 0) + return ret; /* starting MHL */ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); @@ -755,6 +763,15 @@ static const struct dev_pm_ops hdmi_pm_ops = { .runtime_resume = hdmi_runtime_resume, }; +static void hdmi_resource_clear_clocks(struct hdmi_resources *res) +{ + res->hdmi = ERR_PTR(-EINVAL); + res->sclk_hdmi = ERR_PTR(-EINVAL); + res->sclk_pixel = ERR_PTR(-EINVAL); + res->sclk_hdmiphy = ERR_PTR(-EINVAL); + res->hdmiphy = ERR_PTR(-EINVAL); +} + static void hdmi_resources_cleanup(struct hdmi_device *hdev) { struct hdmi_resources *res = &hdev->res; @@ -765,17 +782,18 @@ static void hdmi_resources_cleanup(struct hdmi_device *hdev) regulator_bulk_free(res->regul_count, res->regul_bulk); /* kfree is NULL-safe */ kfree(res->regul_bulk); - if (!IS_ERR_OR_NULL(res->hdmiphy)) + if (!IS_ERR(res->hdmiphy)) clk_put(res->hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_hdmiphy)) + if (!IS_ERR(res->sclk_hdmiphy)) clk_put(res->sclk_hdmiphy); - if (!IS_ERR_OR_NULL(res->sclk_pixel)) + if (!IS_ERR(res->sclk_pixel)) clk_put(res->sclk_pixel); - if (!IS_ERR_OR_NULL(res->sclk_hdmi)) + if (!IS_ERR(res->sclk_hdmi)) clk_put(res->sclk_hdmi); - if (!IS_ERR_OR_NULL(res->hdmi)) + if (!IS_ERR(res->hdmi)) clk_put(res->hdmi); memset(res, 0, sizeof(*res)); + hdmi_resource_clear_clocks(res); } static int hdmi_resources_init(struct hdmi_device *hdev) @@ -793,8 +811,9 @@ static int hdmi_resources_init(struct hdmi_device *hdev) dev_dbg(dev, "HDMI resource init\n"); memset(res, 0, sizeof(*res)); - /* get clocks, power */ + hdmi_resource_clear_clocks(res); + /* get clocks, power */ res->hdmi = clk_get(dev, "hdmi"); if (IS_ERR(res->hdmi)) { dev_err(dev, "failed to get clock 'hdmi'\n"); diff --git a/drivers/media/platform/s5p-tv/mixer_drv.c b/drivers/media/platform/s5p-tv/mixer_drv.c index 5733033a6ea..51805a5e2be 100644 --- a/drivers/media/platform/s5p-tv/mixer_drv.c +++ b/drivers/media/platform/s5p-tv/mixer_drv.c @@ -211,6 +211,15 @@ fail: return ret; } +static void mxr_resource_clear_clocks(struct mxr_resources *res) +{ + res->mixer = ERR_PTR(-EINVAL); + res->vp = ERR_PTR(-EINVAL); + res->sclk_mixer = ERR_PTR(-EINVAL); + res->sclk_hdmi = ERR_PTR(-EINVAL); + res->sclk_dac = ERR_PTR(-EINVAL); +} + static void mxr_release_plat_resources(struct mxr_device *mdev) { free_irq(mdev->res.irq, mdev); @@ -222,15 +231,15 @@ static void mxr_release_clocks(struct mxr_device *mdev) { struct mxr_resources *res = &mdev->res; - if (!IS_ERR_OR_NULL(res->sclk_dac)) + if (!IS_ERR(res->sclk_dac)) clk_put(res->sclk_dac); - if (!IS_ERR_OR_NULL(res->sclk_hdmi)) + if (!IS_ERR(res->sclk_hdmi)) clk_put(res->sclk_hdmi); - if (!IS_ERR_OR_NULL(res->sclk_mixer)) + if (!IS_ERR(res->sclk_mixer)) clk_put(res->sclk_mixer); - if (!IS_ERR_OR_NULL(res->vp)) + if (!IS_ERR(res->vp)) clk_put(res->vp); - if (!IS_ERR_OR_NULL(res->mixer)) + if (!IS_ERR(res->mixer)) clk_put(res->mixer); } @@ -239,6 +248,8 @@ static int mxr_acquire_clocks(struct mxr_device *mdev) struct mxr_resources *res = &mdev->res; struct device *dev = mdev->dev; + mxr_resource_clear_clocks(res); + res->mixer = clk_get(dev, "mixer"); if (IS_ERR(res->mixer)) { mxr_err(mdev, "failed to get clock 'mixer'\n"); @@ -299,6 +310,7 @@ static void mxr_release_resources(struct mxr_device *mdev) mxr_release_clocks(mdev); mxr_release_plat_resources(mdev); memset(&mdev->res, 0, sizeof(mdev->res)); + mxr_resource_clear_clocks(&mdev->res); } static void mxr_release_layers(struct mxr_device *mdev) diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index ef0efdf422f..641b1f071e0 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -81,8 +81,9 @@ int mxr_acquire_video(struct mxr_device *mdev, } mdev->alloc_ctx = vb2_dma_contig_init_ctx(mdev->dev); - if (IS_ERR_OR_NULL(mdev->alloc_ctx)) { + if (IS_ERR(mdev->alloc_ctx)) { mxr_err(mdev, "could not acquire vb2 allocator\n"); + ret = PTR_ERR(mdev->alloc_ctx); goto fail_v4l2_dev; } diff --git a/drivers/media/platform/s5p-tv/sdo_drv.c b/drivers/media/platform/s5p-tv/sdo_drv.c index ab6f9ef8942..0afa90f0f6a 100644 --- a/drivers/media/platform/s5p-tv/sdo_drv.c +++ b/drivers/media/platform/s5p-tv/sdo_drv.c @@ -262,11 +262,21 @@ static int sdo_runtime_resume(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); struct sdo_device *sdev = sd_to_sdev(sd); + int ret; dev_info(dev, "resume\n"); - clk_enable(sdev->sclk_dac); - regulator_enable(sdev->vdac); - regulator_enable(sdev->vdet); + + ret = clk_enable(sdev->sclk_dac); + if (ret < 0) + return ret; + + ret = regulator_enable(sdev->vdac); + if (ret < 0) + goto dac_clk_dis; + + ret = regulator_enable(sdev->vdet); + if (ret < 0) + goto vdac_r_dis; /* software reset */ sdo_write_mask(sdev, SDO_CLKCON, ~0, SDO_TVOUT_SW_RESET); @@ -285,6 +295,12 @@ static int sdo_runtime_resume(struct device *dev) SDO_COMPENSATION_CVBS_COMP_OFF); sdo_reg_debug(sdev); return 0; + +vdac_r_dis: + regulator_disable(sdev->vdac); +dac_clk_dis: + clk_disable(sdev->sclk_dac); + return ret; } static const struct dev_pm_ops sdo_pm_ops = { diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c index 39b77d24b9c..3dd762e5b67 100644 --- a/drivers/media/platform/s5p-tv/sii9234_drv.c +++ b/drivers/media/platform/s5p-tv/sii9234_drv.c @@ -249,7 +249,9 @@ static int sii9234_runtime_resume(struct device *dev) int ret; dev_info(dev, "resume start\n"); - regulator_enable(ctx->power); + ret = regulator_enable(ctx->power); + if (ret < 0) + return ret; ret = sii9234_reset(ctx); if (ret) diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index 59a9deefb24..aa4cca371cb 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -359,10 +359,7 @@ static int sh_veu_context_init(struct sh_veu_dev *veu) veu->m2m_ctx = v4l2_m2m_ctx_init(veu->m2m_dev, veu, sh_veu_queue_init); - if (IS_ERR(veu->m2m_ctx)) - return PTR_ERR(veu->m2m_ctx); - - return 0; + return PTR_RET(veu->m2m_ctx); } static int sh_veu_querycap(struct file *file, void *priv, diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 7d0235069c8..7a9c5e9329f 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -1248,32 +1248,6 @@ static unsigned int sh_vou_poll(struct file *file, poll_table *wait) return res; } -static int sh_vou_g_chip_ident(struct file *file, void *fh, - struct v4l2_dbg_chip_ident *id) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - - return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_chip_ident, id); -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int sh_vou_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - - return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, g_register, reg); -} - -static int sh_vou_s_register(struct file *file, void *fh, - const struct v4l2_dbg_register *reg) -{ - struct sh_vou_device *vou_dev = video_drvdata(file); - - return v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, core, s_register, reg); -} -#endif - /* sh_vou display ioctl operations */ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_querycap = sh_vou_querycap, @@ -1292,11 +1266,6 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = { .vidioc_cropcap = sh_vou_cropcap, .vidioc_g_crop = sh_vou_g_crop, .vidioc_s_crop = sh_vou_s_crop, - .vidioc_g_chip_ident = sh_vou_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = sh_vou_g_register, - .vidioc_s_register = sh_vou_s_register, -#endif }; static const struct v4l2_file_operations sh_vou_fops = { @@ -1313,7 +1282,6 @@ static const struct video_device sh_vou_video_template = { .fops = &sh_vou_fops, .ioctl_ops = &sh_vou_ioctl_ops, .tvnorms = V4L2_STD_525_60, /* PAL only supported in 8-bit non-bt656 mode */ - .current_norm = V4L2_STD_NTSC_M, .vfl_dir = VFL_DIR_TX, }; @@ -1352,7 +1320,7 @@ static int sh_vou_probe(struct platform_device *pdev) pix = &vou_dev->pix; /* Fill in defaults */ - vou_dev->std = sh_vou_video_template.current_norm; + vou_dev->std = V4L2_STD_NTSC_M; rect->left = 0; rect->top = 0; rect->width = VOU_MAX_IMAGE_WIDTH; diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index b139b525bb1..626dcccc37d 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -8,6 +8,9 @@ config SOC_CAMERA over a bus like PCI or USB. For example some i2c camera connected directly to the data bus of an SoC. +config SOC_CAMERA_SCALE_CROP + tristate + config SOC_CAMERA_PLATFORM tristate "platform camera support" depends on SOC_CAMERA @@ -27,14 +30,10 @@ config VIDEO_MX1 ---help--- This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface -config MX3_VIDEO - bool - config VIDEO_MX3 tristate "i.MX3x Camera Sensor Interface driver" depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA select VIDEOBUF2_DMA_CONTIG - select MX3_VIDEO ---help--- This is a v4l2 driver for the i.MX3x Camera Sensor Interface @@ -55,6 +54,7 @@ config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK select VIDEOBUF2_DMA_CONTIG + select SOC_CAMERA_SCALE_CROP ---help--- This is a v4l2 driver for the SuperH Mobile CEU Interface @@ -66,14 +66,10 @@ config VIDEO_OMAP1 ---help--- This is a v4l2 driver for the TI OMAP1 camera interface -config VIDEO_MX2_HOSTSUPPORT - bool - config VIDEO_MX2 tristate "i.MX27 Camera Sensor Interface driver" depends on VIDEO_DEV && SOC_CAMERA && MACH_MX27 select VIDEOBUF2_DMA_CONTIG - select VIDEO_MX2_HOSTSUPPORT ---help--- This is a v4l2 driver for the i.MX27 Camera Sensor Interface diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 136b7f8ff10..39186224c16 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -1,4 +1,8 @@ obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o +obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o + +# a platform subdevice driver stub, allowing to support cameras by adding a +# couple of callback functions to the board code obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers @@ -10,5 +14,3 @@ obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_SH_MOBILE_CSI2) += sh_mobile_csi2.o - -ccflags-y += -I$(srctree)/drivers/media/i2c/soc_camera diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 1abbb36d075..10448563250 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -102,7 +102,6 @@ struct atmel_isi { struct list_head video_buffer_list; struct frame_buffer *active; - struct soc_camera_device *icd; struct soc_camera_host soc_host; }; @@ -367,7 +366,7 @@ static void start_dma(struct atmel_isi *isi, struct frame_buffer *buffer) /* Check if already in a frame */ if (isi_readl(isi, ISI_STATUS) & ISI_CTRL_CDC) { - dev_err(isi->icd->parent, "Already in frame handling.\n"); + dev_err(isi->soc_host.icd->parent, "Already in frame handling.\n"); return; } @@ -746,16 +745,26 @@ static int isi_camera_get_formats(struct soc_camera_device *icd, return formats; } -/* Called with .host_lock held */ static int isi_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void isi_camera_remove_device(struct soc_camera_device *icd) +{ + dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n", + icd->devnum); +} + +/* Called with .host_lock held */ +static int isi_camera_clock_start(struct soc_camera_host *ici) +{ struct atmel_isi *isi = ici->priv; int ret; - if (isi->icd) - return -EBUSY; - ret = clk_enable(isi->pclk); if (ret) return ret; @@ -766,25 +775,16 @@ static int isi_camera_add_device(struct soc_camera_device *icd) return ret; } - isi->icd = icd; - dev_dbg(icd->parent, "Atmel ISI Camera driver attached to camera %d\n", - icd->devnum); return 0; } + /* Called with .host_lock held */ -static void isi_camera_remove_device(struct soc_camera_device *icd) +static void isi_camera_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; - BUG_ON(icd != isi->icd); - clk_disable(isi->mck); clk_disable(isi->pclk); - isi->icd = NULL; - - dev_dbg(icd->parent, "Atmel ISI Camera driver detached from camera %d\n", - icd->devnum); } static unsigned int isi_camera_poll(struct file *file, poll_table *pt) @@ -888,6 +888,8 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = { .owner = THIS_MODULE, .add = isi_camera_add_device, .remove = isi_camera_remove_device, + .clock_start = isi_camera_clock_start, + .clock_stop = isi_camera_clock_stop, .set_fmt = isi_camera_set_fmt, .try_fmt = isi_camera_try_fmt, .get_formats = isi_camera_get_formats, diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index a3fd8d63546..fea3e61476a 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -104,7 +104,6 @@ struct mx1_buffer { */ struct mx1_camera_dev { struct soc_camera_host soc_host; - struct soc_camera_device *icd; struct mx1_camera_pdata *pdata; struct mx1_buffer *active; struct resource *res; @@ -220,7 +219,7 @@ out: static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) { struct videobuf_buffer *vbuf = &pcdev->active->vb; - struct device *dev = pcdev->icd->parent; + struct device *dev = pcdev->soc_host.icd->parent; int ret; if (unlikely(!pcdev->active)) { @@ -331,7 +330,7 @@ static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, static void mx1_camera_dma_irq(int channel, void *data) { struct mx1_camera_dev *pcdev = data; - struct device *dev = pcdev->icd->parent; + struct device *dev = pcdev->soc_host.icd->parent; struct mx1_buffer *buf; struct videobuf_buffer *vb; unsigned long flags; @@ -389,7 +388,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev) */ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - dev_dbg(pcdev->icd->parent, + dev_dbg(pcdev->soc_host.icd->parent, "System clock %lukHz, target freq %dkHz, divisor %lu\n", lcdclk / 1000, mclk / 1000, div); @@ -400,7 +399,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) { unsigned int csicr1 = CSICR1_EN; - dev_dbg(pcdev->icd->parent, "Activate device\n"); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Activate device\n"); clk_prepare_enable(pcdev->clk); @@ -416,7 +415,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) { - dev_dbg(pcdev->icd->parent, "Deactivate device\n"); + dev_dbg(pcdev->soc_host.v4l2_dev.dev, "Deactivate device\n"); /* Disable all CSI interface */ __raw_writel(0x00, pcdev->base + CSICR1); @@ -424,36 +423,38 @@ static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) clk_disable_unprepare(pcdev->clk); } +static int mx1_camera_add_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void mx1_camera_remove_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n", + icd->devnum); +} + /* * The following two functions absolutely depend on the fact, that * there can be only one camera on i.MX1/i.MXL camera sensor interface */ -static int mx1_camera_add_device(struct soc_camera_device *icd) +static int mx1_camera_clock_start(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; - if (pcdev->icd) - return -EBUSY; - - dev_info(icd->parent, "MX1 Camera driver attached to camera %d\n", - icd->devnum); - mx1_camera_activate(pcdev); - pcdev->icd = icd; - return 0; } -static void mx1_camera_remove_device(struct soc_camera_device *icd) +static void mx1_camera_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx1_camera_dev *pcdev = ici->priv; unsigned int csicr1; - BUG_ON(icd != pcdev->icd); - /* disable interrupts */ csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK; __raw_writel(csicr1, pcdev->base + CSICR1); @@ -461,12 +462,7 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd) /* Stop DMA engine */ imx_dma_disable(pcdev->dma_chan); - dev_info(icd->parent, "MX1 Camera driver detached from camera %d\n", - icd->devnum); - mx1_camera_deactivate(pcdev); - - pcdev->icd = NULL; } static int mx1_camera_set_bus_param(struct soc_camera_device *icd) @@ -679,6 +675,8 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = { .owner = THIS_MODULE, .add = mx1_camera_add_device, .remove = mx1_camera_remove_device, + .clock_start = mx1_camera_clock_start, + .clock_stop = mx1_camera_clock_stop, .set_bus_param = mx1_camera_set_bus_param, .set_fmt = mx1_camera_set_fmt, .try_fmt = mx1_camera_try_fmt, diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 5bbeb43e453..45a0276be4e 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -236,7 +236,6 @@ enum mx2_camera_type { struct mx2_camera_dev { struct device *dev; struct soc_camera_host soc_host; - struct soc_camera_device *icd; struct clk *clk_emma_ahb, *clk_emma_ipg; struct clk *clk_csi_ahb, *clk_csi_per; @@ -394,8 +393,8 @@ static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev, writel(phys, pcdev->base_emma + PRP_DEST_Y_PTR - 0x14 * bufnum); if (prp->out_fmt == V4L2_PIX_FMT_YUV420) { - u32 imgsize = pcdev->icd->user_height * - pcdev->icd->user_width; + u32 imgsize = pcdev->soc_host.icd->user_height * + pcdev->soc_host.icd->user_width; writel(phys + imgsize, pcdev->base_emma + PRP_DEST_CB_PTR - 0x14 * bufnum); @@ -413,20 +412,30 @@ static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev) writel(0, pcdev->base_emma + PRP_CNTL); } +static int mx2_camera_add_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void mx2_camera_remove_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "Camera driver detached from camera %d\n", + icd->devnum); +} + /* * The following two functions absolutely depend on the fact, that * there can be only one camera on mx2 camera sensor interface */ -static int mx2_camera_add_device(struct soc_camera_device *icd) +static int mx2_camera_clock_start(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; int ret; u32 csicr1; - if (pcdev->icd) - return -EBUSY; - ret = clk_prepare_enable(pcdev->clk_csi_ahb); if (ret < 0) return ret; @@ -441,12 +450,8 @@ static int mx2_camera_add_device(struct soc_camera_device *icd) pcdev->csicr1 = csicr1; writel(pcdev->csicr1, pcdev->base_csi + CSICR1); - pcdev->icd = icd; pcdev->frame_count = 0; - dev_info(icd->parent, "Camera driver attached to camera %d\n", - icd->devnum); - return 0; exit_csi_ahb: @@ -455,19 +460,11 @@ exit_csi_ahb: return ret; } -static void mx2_camera_remove_device(struct soc_camera_device *icd) +static void mx2_camera_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; - BUG_ON(icd != pcdev->icd); - - dev_info(icd->parent, "Camera driver detached from camera %d\n", - icd->devnum); - mx2_camera_deactivate(pcdev); - - pcdev->icd = NULL; } /* @@ -1280,6 +1277,8 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = { .owner = THIS_MODULE, .add = mx2_camera_add_device, .remove = mx2_camera_remove_device, + .clock_start = mx2_camera_clock_start, + .clock_stop = mx2_camera_clock_stop, .set_fmt = mx2_camera_set_fmt, .set_crop = mx2_camera_set_crop, .get_formats = mx2_camera_get_formats, diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 5da337736cd..1047e3e8db7 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -94,7 +94,6 @@ struct mx3_camera_dev { * Interface. If anyone ever builds hardware to enable more than one * camera _simultaneously_, they will have to modify this driver too */ - struct soc_camera_device *icd; struct clk *clk; void __iomem *base; @@ -461,8 +460,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q, } /* First part of ipu_csi_init_interface() */ -static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, - struct soc_camera_device *icd) +static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam) { u32 conf; long rate; @@ -506,51 +504,49 @@ static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam, clk_prepare_enable(mx3_cam->clk); rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk); - dev_dbg(icd->parent, "Set SENS_CONF to %x, rate %ld\n", conf, rate); + dev_dbg(mx3_cam->soc_host.v4l2_dev.dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate); if (rate) clk_set_rate(mx3_cam->clk, rate); } -/* Called with .host_lock held */ static int mx3_camera_add_device(struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct mx3_camera_dev *mx3_cam = ici->priv; + dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", + icd->devnum); - if (mx3_cam->icd) - return -EBUSY; + return 0; +} - mx3_camera_activate(mx3_cam, icd); +static void mx3_camera_remove_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", + icd->devnum); +} - mx3_cam->buf_total = 0; - mx3_cam->icd = icd; +/* Called with .host_lock held */ +static int mx3_camera_clock_start(struct soc_camera_host *ici) +{ + struct mx3_camera_dev *mx3_cam = ici->priv; - dev_info(icd->parent, "MX3 Camera driver attached to camera %d\n", - icd->devnum); + mx3_camera_activate(mx3_cam); + + mx3_cam->buf_total = 0; return 0; } /* Called with .host_lock held */ -static void mx3_camera_remove_device(struct soc_camera_device *icd) +static void mx3_camera_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - BUG_ON(icd != mx3_cam->icd); - if (*ichan) { dma_release_channel(&(*ichan)->dma_chan); *ichan = NULL; } clk_disable_unprepare(mx3_cam->clk); - - mx3_cam->icd = NULL; - - dev_info(icd->parent, "MX3 Camera driver detached from camera %d\n", - icd->devnum); } static int test_platform_param(struct mx3_camera_dev *mx3_cam, @@ -1133,6 +1129,8 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { .owner = THIS_MODULE, .add = mx3_camera_add_device, .remove = mx3_camera_remove_device, + .clock_start = mx3_camera_clock_start, + .clock_stop = mx3_camera_clock_stop, .set_crop = mx3_camera_set_crop, .set_fmt = mx3_camera_set_fmt, .try_fmt = mx3_camera_try_fmt, diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index 9689a6e89b7..6769193c7c7 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -150,7 +150,6 @@ struct omap1_cam_buf { struct omap1_cam_dev { struct soc_camera_host soc_host; - struct soc_camera_device *icd; struct clk *clk; unsigned int irq; @@ -564,7 +563,7 @@ static void videobuf_done(struct omap1_cam_dev *pcdev, { struct omap1_cam_buf *buf = pcdev->active; struct videobuf_buffer *vb; - struct device *dev = pcdev->icd->parent; + struct device *dev = pcdev->soc_host.icd->parent; if (WARN_ON(!buf)) { suspend_capture(pcdev); @@ -790,7 +789,7 @@ out: static irqreturn_t cam_isr(int irq, void *data) { struct omap1_cam_dev *pcdev = data; - struct device *dev = pcdev->icd->parent; + struct device *dev = pcdev->soc_host.icd->parent; struct omap1_cam_buf *buf = pcdev->active; u32 it_status; unsigned long flags; @@ -894,19 +893,29 @@ static void sensor_reset(struct omap1_cam_dev *pcdev, bool reset) CAM_WRITE(pcdev, GPIO, !reset); } +static int omap1_cam_add_device(struct soc_camera_device *icd) +{ + dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void omap1_cam_remove_device(struct soc_camera_device *icd) +{ + dev_dbg(icd->parent, + "OMAP1 Camera driver detached from camera %d\n", icd->devnum); +} + /* * The following two functions absolutely depend on the fact, that * there can be only one camera on OMAP1 camera sensor interface */ -static int omap1_cam_add_device(struct soc_camera_device *icd) +static int omap1_cam_clock_start(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; - if (pcdev->icd) - return -EBUSY; - clk_enable(pcdev->clk); /* setup sensor clock */ @@ -941,21 +950,14 @@ static int omap1_cam_add_device(struct soc_camera_device *icd) sensor_reset(pcdev, false); - pcdev->icd = icd; - - dev_dbg(icd->parent, "OMAP1 Camera driver attached to camera %d\n", - icd->devnum); return 0; } -static void omap1_cam_remove_device(struct soc_camera_device *icd) +static void omap1_cam_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; u32 ctrlclock; - BUG_ON(icd != pcdev->icd); - suspend_capture(pcdev); disable_capture(pcdev); @@ -973,11 +975,6 @@ static void omap1_cam_remove_device(struct soc_camera_device *icd) CAM_WRITE(pcdev, CTRLCLOCK, ctrlclock & ~MCLK_EN); clk_disable(pcdev->clk); - - pcdev->icd = NULL; - - dev_dbg(icd->parent, - "OMAP1 Camera driver detached from camera %d\n", icd->devnum); } /* Duplicate standard formats based on host capability of byte swapping */ @@ -1535,6 +1532,8 @@ static struct soc_camera_host_ops omap1_host_ops = { .owner = THIS_MODULE, .add = omap1_cam_add_device, .remove = omap1_cam_remove_device, + .clock_start = omap1_cam_clock_start, + .clock_stop = omap1_cam_clock_stop, .get_formats = omap1_cam_get_formats, .set_crop = omap1_cam_set_crop, .set_fmt = omap1_cam_set_fmt, diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index d665242e820..d4df305fcc1 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -200,7 +200,6 @@ struct pxa_camera_dev { * interface. If anyone ever builds hardware to enable more than * one camera, they will have to modify this driver too */ - struct soc_camera_device *icd; struct clk *clk; unsigned int irq; @@ -956,40 +955,39 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) return IRQ_HANDLED; } +static int pxa_camera_add_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "PXA Camera driver attached to camera %d\n", + icd->devnum); + + return 0; +} + +static void pxa_camera_remove_device(struct soc_camera_device *icd) +{ + dev_info(icd->parent, "PXA Camera driver detached from camera %d\n", + icd->devnum); +} + /* * The following two functions absolutely depend on the fact, that * there can be only one camera on PXA quick capture interface * Called with .host_lock held */ -static int pxa_camera_add_device(struct soc_camera_device *icd) +static int pxa_camera_clock_start(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; - if (pcdev->icd) - return -EBUSY; - pxa_camera_activate(pcdev); - pcdev->icd = icd; - - dev_info(icd->parent, "PXA Camera driver attached to camera %d\n", - icd->devnum); - return 0; } /* Called with .host_lock held */ -static void pxa_camera_remove_device(struct soc_camera_device *icd) +static void pxa_camera_clock_stop(struct soc_camera_host *ici) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct pxa_camera_dev *pcdev = ici->priv; - BUG_ON(icd != pcdev->icd); - - dev_info(icd->parent, "PXA Camera driver detached from camera %d\n", - icd->devnum); - /* disable capture, disable interrupts */ __raw_writel(0x3ff, pcdev->base + CICR0); @@ -999,8 +997,6 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) DCSR(pcdev->dma_chans[2]) = 0; pxa_camera_deactivate(pcdev); - - pcdev->icd = NULL; } static int test_platform_param(struct pxa_camera_dev *pcdev, @@ -1596,8 +1592,8 @@ static int pxa_camera_suspend(struct device *dev) pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR3); pcdev->save_cicr[i++] = __raw_readl(pcdev->base + CICR4); - if (pcdev->icd) { - struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd); + if (pcdev->soc_host.icd) { + struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd); ret = v4l2_subdev_call(sd, core, s_power, 0); if (ret == -ENOIOCTLCMD) ret = 0; @@ -1622,8 +1618,8 @@ static int pxa_camera_resume(struct device *dev) __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR3); __raw_writel(pcdev->save_cicr[i++], pcdev->base + CICR4); - if (pcdev->icd) { - struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->icd); + if (pcdev->soc_host.icd) { + struct v4l2_subdev *sd = soc_camera_to_subdev(pcdev->soc_host.icd); ret = v4l2_subdev_call(sd, core, s_power, 1); if (ret == -ENOIOCTLCMD) ret = 0; @@ -1640,6 +1636,8 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .owner = THIS_MODULE, .add = pxa_camera_add_device, .remove = pxa_camera_remove_device, + .clock_start = pxa_camera_clock_start, + .clock_stop = pxa_camera_clock_stop, .set_crop = pxa_camera_set_crop, .get_formats = pxa_camera_get_formats, .put_formats = pxa_camera_put_formats, diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 143d29fe013..f2de0066089 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -27,6 +27,7 @@ #include <linux/kernel.h> #include <linux/mm.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/time.h> #include <linux/slab.h> #include <linux/device.h> @@ -35,6 +36,7 @@ #include <linux/pm_runtime.h> #include <linux/sched.h> +#include <media/v4l2-async.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> #include <media/soc_camera.h> @@ -44,6 +46,8 @@ #include <media/v4l2-mediabus.h> #include <media/soc_mediabus.h> +#include "soc_scale_crop.h" + /* register offsets for sh7722 / sh7723 */ #define CAPSR 0x00 /* Capture start register */ @@ -95,7 +99,10 @@ struct sh_mobile_ceu_buffer { struct sh_mobile_ceu_dev { struct soc_camera_host ici; - struct soc_camera_device *icd; + /* Asynchronous CSI2 linking */ + struct v4l2_async_subdev *csi2_asd; + struct v4l2_subdev *csi2_sd; + /* Synchronous probing compatibility */ struct platform_device *csi2_pdev; unsigned int irq; @@ -119,6 +126,7 @@ struct sh_mobile_ceu_dev { enum v4l2_field field; int sequence; + unsigned long flags; unsigned int image_mode:1; unsigned int is_16bit:1; @@ -163,7 +171,6 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) { int i, success = 0; - struct soc_camera_device *icd = pcdev->icd; ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ @@ -185,9 +192,8 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) udelay(1); } - if (2 != success) { - dev_warn(icd->pdev, "soft reset time out\n"); + dev_warn(pcdev->ici.v4l2_dev.dev, "soft reset time out\n"); return -EIO; } @@ -277,7 +283,7 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, */ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) { - struct soc_camera_device *icd = pcdev->icd; + struct soc_camera_device *icd = pcdev->ici.icd; dma_addr_t phys_addr_top, phys_addr_bottom; unsigned long top1, top2; unsigned long bottom1, bottom2; @@ -534,72 +540,92 @@ static struct v4l2_subdev *find_csi2(struct sh_mobile_ceu_dev *pcdev) { struct v4l2_subdev *sd; - if (!pcdev->csi2_pdev) - return NULL; + if (pcdev->csi2_sd) + return pcdev->csi2_sd; - v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) - if (&pcdev->csi2_pdev->dev == v4l2_get_subdevdata(sd)) - return sd; + if (pcdev->csi2_asd) { + char name[] = "sh-mobile-csi2"; + v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) + if (!strncmp(name, sd->name, sizeof(name) - 1)) { + pcdev->csi2_sd = sd; + return sd; + } + } return NULL; } -/* Called with .host_lock held */ +static struct v4l2_subdev *csi2_subdev(struct sh_mobile_ceu_dev *pcdev, + struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = pcdev->csi2_sd; + + return sd && sd->grp_id == soc_camera_grp_id(icd) ? sd : NULL; +} + static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct v4l2_subdev *csi2_sd; + struct v4l2_subdev *csi2_sd = find_csi2(pcdev); int ret; - if (pcdev->icd) - return -EBUSY; - - dev_info(icd->parent, - "SuperH Mobile CEU driver attached to camera %d\n", - icd->devnum); - - pm_runtime_get_sync(ici->v4l2_dev.dev); - - pcdev->buf_total = 0; - - ret = sh_mobile_ceu_soft_reset(pcdev); - - csi2_sd = find_csi2(pcdev); if (csi2_sd) { csi2_sd->grp_id = soc_camera_grp_id(icd); v4l2_set_subdev_hostdata(csi2_sd, icd); } ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - pm_runtime_put(ici->v4l2_dev.dev); + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) return ret; - } /* * -ENODEV is special: either csi2_sd == NULL or the CSI-2 driver * has not found this soc-camera device among its clients */ - if (ret == -ENODEV && csi2_sd) + if (csi2_sd && ret == -ENODEV) csi2_sd->grp_id = 0; - pcdev->icd = icd; + + dev_info(icd->parent, + "SuperH Mobile CEU%s driver attached to camera %d\n", + csi2_sd && csi2_sd->grp_id ? "/CSI-2" : "", icd->devnum); return 0; } -/* Called with .host_lock held */ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; struct v4l2_subdev *csi2_sd = find_csi2(pcdev); - BUG_ON(icd != pcdev->icd); + dev_info(icd->parent, + "SuperH Mobile CEU driver detached from camera %d\n", + icd->devnum); v4l2_subdev_call(csi2_sd, core, s_power, 0); - if (csi2_sd) - csi2_sd->grp_id = 0; +} + +/* Called with .host_lock held */ +static int sh_mobile_ceu_clock_start(struct soc_camera_host *ici) +{ + struct sh_mobile_ceu_dev *pcdev = ici->priv; + int ret; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + pcdev->buf_total = 0; + + ret = sh_mobile_ceu_soft_reset(pcdev); + + return 0; +} + +/* Called with .host_lock held */ +static void sh_mobile_ceu_clock_stop(struct soc_camera_host *ici) +{ + struct sh_mobile_ceu_dev *pcdev = ici->priv; + /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); sh_mobile_ceu_soft_reset(pcdev); @@ -614,12 +640,6 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) spin_unlock_irq(&pcdev->lock); pm_runtime_put(ici->v4l2_dev.dev); - - dev_info(icd->parent, - "SuperH Mobile CEU driver detached from camera %d\n", - icd->devnum); - - pcdev->icd = NULL; } /* @@ -705,7 +725,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) } /* CSI2 special configuration */ - if (pcdev->pdata->csi2) { + if (csi2_subdev(pcdev, icd)) { in_width = ((in_width - 2) * 2); left_offset *= 2; } @@ -762,13 +782,7 @@ static void capture_restore(struct sh_mobile_ceu_dev *pcdev, u32 capsr) static struct v4l2_subdev *find_bus_subdev(struct sh_mobile_ceu_dev *pcdev, struct soc_camera_device *icd) { - if (pcdev->csi2_pdev) { - struct v4l2_subdev *csi2_sd = find_csi2(pcdev); - if (csi2_sd && csi2_sd->grp_id == soc_camera_grp_id(icd)) - return csi2_sd; - } - - return soc_camera_to_subdev(icd); + return csi2_subdev(pcdev, icd) ? : soc_camera_to_subdev(icd); } #define CEU_BUS_FLAGS (V4L2_MBUS_MASTER | \ @@ -809,7 +823,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) /* Make choises, based on platform preferences */ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { - if (pcdev->pdata->flags & SH_CEU_FLAG_HSYNC_LOW) + if (pcdev->flags & SH_CEU_FLAG_HSYNC_LOW) common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; @@ -817,7 +831,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { - if (pcdev->pdata->flags & SH_CEU_FLAG_VSYNC_LOW) + if (pcdev->flags & SH_CEU_FLAG_VSYNC_LOW) common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; else common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; @@ -872,11 +886,11 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - if (pcdev->pdata->csi2) /* CSI2 mode */ + if (csi2_subdev(pcdev, icd)) /* CSI2 mode */ value |= 3 << 12; else if (pcdev->is_16bit) value |= 1 << 12; - else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT) + else if (pcdev->flags & SH_CEU_FLAG_LOWER_8BIT) value |= 2 << 12; ceu_write(pcdev, CAMCR, value); @@ -993,8 +1007,6 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) fmt->packing == SOC_MBUS_PACKING_EXTEND16); } -static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); - static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) { return container_of(ctrl->handler, struct soc_camera_device, @@ -1051,7 +1063,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int return 0; } - if (!pcdev->pdata->csi2) { + if (!csi2_subdev(pcdev, icd)) { /* Are there any restrictions in the CSI-2 case? */ ret = sh_mobile_ceu_try_bus_param(icd, fmt->bits_per_sample); if (ret < 0) @@ -1072,7 +1084,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int /* FIXME: subwindow is lost between close / open */ /* Cache current client geometry */ - ret = client_g_rect(sd, &rect); + ret = soc_camera_client_g_rect(sd, &rect); if (ret < 0) return ret; @@ -1182,334 +1194,8 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) icd->host_priv = NULL; } -/* Check if any dimension of r1 is smaller than respective one of r2 */ -static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->width < r2->width || r1->height < r2->height; -} - -/* Check if r1 fails to cover r2 */ -static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) -{ - return r1->left > r2->left || r1->top > r2->top || - r1->left + r1->width < r2->left + r2->width || - r1->top + r1->height < r2->top + r2->height; -} - -static unsigned int scale_down(unsigned int size, unsigned int scale) -{ - return (size * 4096 + scale / 2) / scale; -} - -static unsigned int calc_generic_scale(unsigned int input, unsigned int output) -{ - return (input * 4096 + output / 2) / output; -} - -/* Get and store current client crop */ -static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) -{ - struct v4l2_crop crop; - struct v4l2_cropcap cap; - int ret; - - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, g_crop, &crop); - if (!ret) { - *rect = crop.c; - return ret; - } - - /* Camera driver doesn't support .g_crop(), assume default rectangle */ - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, cropcap, &cap); - if (!ret) - *rect = cap.defrect; - - return ret; -} - -/* Client crop has changed, update our sub-rectangle to remain within the area */ -static void update_subrect(struct sh_mobile_ceu_cam *cam) -{ - struct v4l2_rect *rect = &cam->rect, *subrect = &cam->subrect; - - if (rect->width < subrect->width) - subrect->width = rect->width; - - if (rect->height < subrect->height) - subrect->height = rect->height; - - if (rect->left > subrect->left) - subrect->left = rect->left; - else if (rect->left + rect->width > - subrect->left + subrect->width) - subrect->left = rect->left + rect->width - - subrect->width; - - if (rect->top > subrect->top) - subrect->top = rect->top; - else if (rect->top + rect->height > - subrect->top + subrect->height) - subrect->top = rect->top + rect->height - - subrect->height; -} - -/* - * The common for both scaling and cropping iterative approach is: - * 1. try if the client can produce exactly what requested by the user - * 2. if (1) failed, try to double the client image until we get one big enough - * 3. if (2) failed, try to request the maximum image - */ -static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, - struct v4l2_crop *cam_crop) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; - struct device *dev = sd->v4l2_dev->dev; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_cropcap cap; - int ret; - unsigned int width, height; - - v4l2_subdev_call(sd, video, s_crop, crop); - ret = client_g_rect(sd, cam_rect); - if (ret < 0) - return ret; - - /* - * Now cam_crop contains the current camera input rectangle, and it must - * be within camera cropcap bounds - */ - if (!memcmp(rect, cam_rect, sizeof(*rect))) { - /* Even if camera S_CROP failed, but camera rectangle matches */ - dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", - rect->width, rect->height, rect->left, rect->top); - cam->rect = *cam_rect; - return 0; - } - - /* Try to fix cropping, that camera hasn't managed to set */ - dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top, - rect->width, rect->height, rect->left, rect->top); - - /* We need sensor maximum rectangle */ - ret = v4l2_subdev_call(sd, video, cropcap, &cap); - if (ret < 0) - return ret; - - /* Put user requested rectangle within sensor bounds */ - soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, - cap.bounds.width); - soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, - cap.bounds.height); - - /* - * Popular special case - some cameras can only handle fixed sizes like - * QVGA, VGA,... Take care to avoid infinite loop. - */ - width = max(cam_rect->width, 2); - height = max(cam_rect->height, 2); - - /* - * Loop as long as sensor is not covering the requested rectangle and - * is still within its bounds - */ - while (!ret && (is_smaller(cam_rect, rect) || - is_inside(cam_rect, rect)) && - (cap.bounds.width > width || cap.bounds.height > height)) { - - width *= 2; - height *= 2; - - cam_rect->width = width; - cam_rect->height = height; - - /* - * We do not know what capabilities the camera has to set up - * left and top borders. We could try to be smarter in iterating - * them, e.g., if camera current left is to the right of the - * target left, set it to the middle point between the current - * left and minimum left. But that would add too much - * complexity: we would have to iterate each border separately. - * Instead we just drop to the left and top bounds. - */ - if (cam_rect->left > rect->left) - cam_rect->left = cap.bounds.left; - - if (cam_rect->left + cam_rect->width < rect->left + rect->width) - cam_rect->width = rect->left + rect->width - - cam_rect->left; - - if (cam_rect->top > rect->top) - cam_rect->top = cap.bounds.top; - - if (cam_rect->top + cam_rect->height < rect->top + rect->height) - cam_rect->height = rect->top + rect->height - - cam_rect->top; - - v4l2_subdev_call(sd, video, s_crop, cam_crop); - ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - /* S_CROP must not modify the rectangle */ - if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { - /* - * The camera failed to configure a suitable cropping, - * we cannot use the current rectangle, set to max - */ - *cam_rect = cap.bounds; - v4l2_subdev_call(sd, video, s_crop, cam_crop); - ret = client_g_rect(sd, cam_rect); - dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, - cam_rect->width, cam_rect->height, - cam_rect->left, cam_rect->top); - } - - if (!ret) { - cam->rect = *cam_rect; - update_subrect(cam); - } - - return ret; -} - -/* Iterative s_mbus_fmt, also updates cached client crop on success */ -static int client_s_fmt(struct soc_camera_device *icd, - struct v4l2_mbus_framefmt *mf, bool ceu_can_scale) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - struct device *dev = icd->parent; - unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; - unsigned int max_width, max_height; - struct v4l2_cropcap cap; - bool ceu_1to1; - int ret; - - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, mf); - if (ret < 0) - return ret; - - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); - - if (width == mf->width && height == mf->height) { - /* Perfect! The client has done it all. */ - ceu_1to1 = true; - goto update_cache; - } - - ceu_1to1 = false; - if (!ceu_can_scale) - goto update_cache; - - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = v4l2_subdev_call(sd, video, cropcap, &cap); - if (ret < 0) - return ret; - - max_width = min(cap.bounds.width, pcdev->max_width); - max_height = min(cap.bounds.height, pcdev->max_height); - - /* Camera set a format, but geometry is not precise, try to improve */ - tmp_w = mf->width; - tmp_h = mf->height; - - /* width <= max_width && height <= max_height - guaranteed by try_fmt */ - while ((width > tmp_w || height > tmp_h) && - tmp_w < max_width && tmp_h < max_height) { - tmp_w = min(2 * tmp_w, max_width); - tmp_h = min(2 * tmp_h, max_height); - mf->width = tmp_w; - mf->height = tmp_h; - ret = v4l2_device_call_until_err(sd->v4l2_dev, - soc_camera_grp_id(icd), video, - s_mbus_fmt, mf); - dev_geo(dev, "Camera scaled to %ux%u\n", - mf->width, mf->height); - if (ret < 0) { - /* This shouldn't happen */ - dev_err(dev, "Client failed to set format: %d\n", ret); - return ret; - } - } - -update_cache: - /* Update cache */ - ret = client_g_rect(sd, &cam->rect); - if (ret < 0) - return ret; - - if (ceu_1to1) - cam->subrect = cam->rect; - else - update_subrect(cam); - - return 0; -} - -/** - * @width - on output: user width, mapped back to input - * @height - on output: user height, mapped back to input - * @mf - in- / output camera output window - */ -static int client_scale(struct soc_camera_device *icd, - struct v4l2_mbus_framefmt *mf, - unsigned int *width, unsigned int *height, - bool ceu_can_scale) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct device *dev = icd->parent; - struct v4l2_mbus_framefmt mf_tmp = *mf; - unsigned int scale_h, scale_v; - int ret; - - /* - * 5. Apply iterative camera S_FMT for camera user window (also updates - * client crop cache and the imaginary sub-rectangle). - */ - ret = client_s_fmt(icd, &mf_tmp, ceu_can_scale); - if (ret < 0) - return ret; - - dev_geo(dev, "5: camera scaled to %ux%u\n", - mf_tmp.width, mf_tmp.height); - - /* 6. Retrieve camera output window (g_fmt) */ - - /* unneeded - it is already in "mf_tmp" */ - - /* 7. Calculate new client scales. */ - scale_h = calc_generic_scale(cam->rect.width, mf_tmp.width); - scale_v = calc_generic_scale(cam->rect.height, mf_tmp.height); - - mf->width = mf_tmp.width; - mf->height = mf_tmp.height; - mf->colorspace = mf_tmp.colorspace; - - /* - * 8. Calculate new CEU crop - apply camera scales to previously - * updated "effective" crop. - */ - *width = scale_down(cam->subrect.width, scale_h); - *height = scale_down(cam->subrect.height, scale_v); - - dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); - - return 0; -} +#define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale) +#define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out) /* * CEU can scale and crop, but we don't want to waste bandwidth and kill the @@ -1547,7 +1233,8 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, * 1. - 2. Apply iterative camera S_CROP for new input window, read back * actual camera rectangle. */ - ret = client_s_crop(icd, &a_writable, &cam_crop); + ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop, + &cam->rect, &cam->subrect); if (ret < 0) return ret; @@ -1666,55 +1353,6 @@ static int sh_mobile_ceu_get_crop(struct soc_camera_device *icd, return 0; } -/* - * Calculate real client output window by applying new scales to the current - * client crop. New scales are calculated from the requested output format and - * CEU crop, mapped backed onto the client input (subrect). - */ -static void calculate_client_output(struct soc_camera_device *icd, - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf) -{ - struct sh_mobile_ceu_cam *cam = icd->host_priv; - struct device *dev = icd->parent; - struct v4l2_rect *cam_subrect = &cam->subrect; - unsigned int scale_v, scale_h; - - if (cam_subrect->width == cam->rect.width && - cam_subrect->height == cam->rect.height) { - /* No sub-cropping */ - mf->width = pix->width; - mf->height = pix->height; - return; - } - - /* 1.-2. Current camera scales and subwin - cached. */ - - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", - cam_subrect->width, cam_subrect->height, - cam_subrect->left, cam_subrect->top); - - /* - * 3. Calculate new combined scales from input sub-window to requested - * user window. - */ - - /* - * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF - * (128x96) or larger than VGA - */ - scale_h = calc_generic_scale(cam_subrect->width, pix->width); - scale_v = calc_generic_scale(cam_subrect->height, pix->height); - - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); - - /* - * 4. Calculate desired client output window by applying combined scales - * to client (real) input window. - */ - mf->width = scale_down(cam->rect.width, scale_h); - mf->height = scale_down(cam->rect.height, scale_v); -} - /* Similar to set_crop multistage iterative algorithm */ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_format *f) @@ -1727,8 +1365,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, struct v4l2_mbus_framefmt mf; __u32 pixfmt = pix->pixelformat; const struct soc_camera_format_xlate *xlate; - /* Keep Compiler Happy */ - unsigned int ceu_sub_width = 0, ceu_sub_height = 0; + unsigned int ceu_sub_width = pcdev->max_width, + ceu_sub_height = pcdev->max_height; u16 scale_v, scale_h; int ret; bool image_mode; @@ -1755,7 +1393,7 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, } /* 1.-4. Calculate desired client output geometry */ - calculate_client_output(icd, pix, &mf); + soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, 12); mf.field = pix->field; mf.colorspace = pix->colorspace; mf.code = xlate->code; @@ -1777,8 +1415,9 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, dev_geo(dev, "4: request camera output %ux%u\n", mf.width, mf.height); /* 5. - 9. */ - ret = client_scale(icd, &mf, &ceu_sub_width, &ceu_sub_height, - image_mode && V4L2_FIELD_NONE == field); + ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, + &mf, &ceu_sub_width, &ceu_sub_height, + image_mode && V4L2_FIELD_NONE == field, 12); dev_geo(dev, "5-9: client scale return %d\n", ret); @@ -2036,6 +1675,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, + .clock_start = sh_mobile_ceu_clock_start, + .clock_stop = sh_mobile_ceu_clock_stop, .get_formats = sh_mobile_ceu_get_formats, .put_formats = sh_mobile_ceu_put_formats, .get_crop = sh_mobile_ceu_get_crop, @@ -2079,7 +1720,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; unsigned int irq; - int err = 0; + int err, i; struct bus_wait wait = { .completion = COMPLETION_INITIALIZER_ONSTACK(wait.completion), .notifier.notifier_call = bus_notify, @@ -2104,13 +1745,36 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) init_completion(&pcdev->complete); pcdev->pdata = pdev->dev.platform_data; - if (!pcdev->pdata) { + if (!pcdev->pdata && !pdev->dev.of_node) { dev_err(&pdev->dev, "CEU platform data not set.\n"); return -EINVAL; } - pcdev->max_width = pcdev->pdata->max_width ? : 2560; - pcdev->max_height = pcdev->pdata->max_height ? : 1920; + /* TODO: implement per-device bus flags */ + if (pcdev->pdata) { + pcdev->max_width = pcdev->pdata->max_width; + pcdev->max_height = pcdev->pdata->max_height; + pcdev->flags = pcdev->pdata->flags; + } + + if (!pcdev->max_width) { + unsigned int v; + err = of_property_read_u32(pdev->dev.of_node, "renesas,max-width", &v); + if (!err) + pcdev->max_width = v; + + if (!pcdev->max_width) + pcdev->max_width = 2560; + } + if (!pcdev->max_height) { + unsigned int v; + err = of_property_read_u32(pdev->dev.of_node, "renesas,max-height", &v); + if (!err) + pcdev->max_height = v; + + if (!pcdev->max_height) + pcdev->max_height = 1920; + } base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) @@ -2160,31 +1824,60 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_free_clk; } - err = soc_camera_host_register(&pcdev->ici); - if (err) - goto exit_free_ctx; + if (pcdev->pdata && pcdev->pdata->asd_sizes) { + struct v4l2_async_subdev **asd; + char name[] = "sh-mobile-csi2"; + int j; + + /* + * CSI2 interfacing: several groups can use CSI2, pick up the + * first one + */ + asd = pcdev->pdata->asd; + for (j = 0; pcdev->pdata->asd_sizes[j]; j++) { + for (i = 0; i < pcdev->pdata->asd_sizes[j]; i++, asd++) { + dev_dbg(&pdev->dev, "%s(): subdev #%d, type %u\n", + __func__, i, (*asd)->bus_type); + if ((*asd)->bus_type == V4L2_ASYNC_BUS_PLATFORM && + !strncmp(name, (*asd)->match.platform.name, + sizeof(name) - 1)) { + pcdev->csi2_asd = *asd; + break; + } + } + if (pcdev->csi2_asd) + break; + } - /* CSI2 interfacing */ - csi2 = pcdev->pdata->csi2; + pcdev->ici.asd = pcdev->pdata->asd; + pcdev->ici.asd_sizes = pcdev->pdata->asd_sizes; + } + + /* Legacy CSI2 interfacing */ + csi2 = pcdev->pdata ? pcdev->pdata->csi2 : NULL; if (csi2) { + /* + * TODO: remove this once all users are converted to + * asynchronous CSI2 probing. If it has to be kept, csi2 + * platform device resources have to be added, using + * platform_device_add_resources() + */ struct platform_device *csi2_pdev = platform_device_alloc("sh-mobile-csi2", csi2->id); struct sh_csi2_pdata *csi2_pdata = csi2->platform_data; if (!csi2_pdev) { err = -ENOMEM; - goto exit_host_unregister; + goto exit_free_ctx; } pcdev->csi2_pdev = csi2_pdev; - err = platform_device_add_data(csi2_pdev, csi2_pdata, sizeof(*csi2_pdata)); + err = platform_device_add_data(csi2_pdev, csi2_pdata, + sizeof(*csi2_pdata)); if (err < 0) goto exit_pdev_put; - csi2_pdata = csi2_pdev->dev.platform_data; - csi2_pdata->v4l2_dev = &pcdev->ici.v4l2_dev; - csi2_pdev->resource = csi2->resource; csi2_pdev->num_resources = csi2->num_resources; @@ -2226,17 +1919,38 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) err = -ENODEV; goto exit_pdev_unregister; } + + pcdev->csi2_sd = platform_get_drvdata(csi2_pdev); + } + + err = soc_camera_host_register(&pcdev->ici); + if (err) + goto exit_csi2_unregister; + + if (csi2) { + err = v4l2_device_register_subdev(&pcdev->ici.v4l2_dev, + pcdev->csi2_sd); + dev_dbg(&pdev->dev, "%s(): ret(register_subdev) = %d\n", + __func__, err); + if (err < 0) + goto exit_host_unregister; + /* v4l2_device_register_subdev() took a reference too */ + module_put(pcdev->csi2_sd->owner); } return 0; -exit_pdev_unregister: - platform_device_del(pcdev->csi2_pdev); -exit_pdev_put: - pcdev->csi2_pdev->resource = NULL; - platform_device_put(pcdev->csi2_pdev); exit_host_unregister: soc_camera_host_unregister(&pcdev->ici); +exit_csi2_unregister: + if (csi2) { + module_put(pcdev->csi2_pdev->dev.driver->owner); +exit_pdev_unregister: + platform_device_del(pcdev->csi2_pdev); +exit_pdev_put: + pcdev->csi2_pdev->resource = NULL; + platform_device_put(pcdev->csi2_pdev); + } exit_free_ctx: vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx); exit_free_clk: @@ -2287,10 +2001,18 @@ static const struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { .runtime_resume = sh_mobile_ceu_runtime_nop, }; +static const struct of_device_id sh_mobile_ceu_of_match[] = { + { .compatible = "renesas,sh-mobile-ceu" }, + { } +}; +MODULE_DEVICE_TABLE(of, sh_mobile_ceu_of_match); + static struct platform_driver sh_mobile_ceu_driver = { .driver = { .name = "sh_mobile_ceu", + .owner = THIS_MODULE, .pm = &sh_mobile_ceu_dev_pm_ops, + .of_match_table = sh_mobile_ceu_of_match, }, .probe = sh_mobile_ceu_probe, .remove = sh_mobile_ceu_remove, @@ -2314,5 +2036,5 @@ module_exit(sh_mobile_ceu_exit); MODULE_DESCRIPTION("SuperH Mobile CEU driver"); MODULE_AUTHOR("Magnus Damm"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.6"); +MODULE_VERSION("0.1.0"); MODULE_ALIAS("platform:sh_mobile_ceu"); diff --git a/drivers/media/platform/soc_camera/sh_mobile_csi2.c b/drivers/media/platform/soc_camera/sh_mobile_csi2.c index 09cb4fc88f3..05dd21a35d6 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_csi2.c +++ b/drivers/media/platform/soc_camera/sh_mobile_csi2.c @@ -36,7 +36,6 @@ struct sh_csi2 { struct v4l2_subdev subdev; - struct list_head list; unsigned int irq; unsigned long mipi_flags; void __iomem *base; @@ -44,6 +43,8 @@ struct sh_csi2 { struct sh_csi2_client_config *client; }; +static void sh_csi2_hwinit(struct sh_csi2 *priv); + static int sh_csi2_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { @@ -132,10 +133,58 @@ static int sh_csi2_s_fmt(struct v4l2_subdev *sd, static int sh_csi2_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | - V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_PARALLEL; + struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); + + if (!priv->mipi_flags) { + struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); + struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); + struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; + unsigned long common_flags, csi2_flags; + struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; + int ret; + + /* Check if we can support this camera */ + csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | + V4L2_MBUS_CSI2_1_LANE; + + switch (pdata->type) { + case SH_CSI2C: + if (priv->client->lanes != 1) + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; + break; + case SH_CSI2I: + switch (priv->client->lanes) { + default: + csi2_flags |= V4L2_MBUS_CSI2_4_LANE; + case 3: + csi2_flags |= V4L2_MBUS_CSI2_3_LANE; + case 2: + csi2_flags |= V4L2_MBUS_CSI2_2_LANE; + } + } + + ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &client_cfg); + if (ret == -ENOIOCTLCMD) + common_flags = csi2_flags; + else if (!ret) + common_flags = soc_mbus_config_compatible(&client_cfg, + csi2_flags); + else + common_flags = 0; + + if (!common_flags) + return -EINVAL; + + /* All good: camera MIPI configuration supported */ + priv->mipi_flags = common_flags; + } + + if (cfg) { + cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | + V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_PARALLEL; + } return 0; } @@ -146,8 +195,17 @@ static int sh_csi2_s_mbus_config(struct v4l2_subdev *sd, struct sh_csi2 *priv = container_of(sd, struct sh_csi2, subdev); struct soc_camera_device *icd = v4l2_get_subdev_hostdata(sd); struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); - struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2, - .flags = priv->mipi_flags}; + struct v4l2_mbus_config client_cfg = {.type = V4L2_MBUS_CSI2,}; + int ret = sh_csi2_g_mbus_config(sd, NULL); + + if (ret < 0) + return ret; + + pm_runtime_get_sync(&priv->pdev->dev); + + sh_csi2_hwinit(priv); + + client_cfg.flags = priv->mipi_flags; return v4l2_subdev_call(client_sd, video, s_mbus_config, &client_cfg); } @@ -202,19 +260,19 @@ static void sh_csi2_hwinit(struct sh_csi2 *priv) static int sh_csi2_client_connect(struct sh_csi2 *priv) { - struct sh_csi2_pdata *pdata = priv->pdev->dev.platform_data; - struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev); - struct v4l2_subdev *client_sd = soc_camera_to_subdev(icd); struct device *dev = v4l2_get_subdevdata(&priv->subdev); - struct v4l2_mbus_config cfg; - unsigned long common_flags, csi2_flags; - int i, ret; + struct sh_csi2_pdata *pdata = dev->platform_data; + struct soc_camera_device *icd = v4l2_get_subdev_hostdata(&priv->subdev); + int i; if (priv->client) return -EBUSY; for (i = 0; i < pdata->num_clients; i++) - if (&pdata->clients[i].pdev->dev == icd->pdev) + if ((pdata->clients[i].pdev && + &pdata->clients[i].pdev->dev == icd->pdev) || + (icd->control && + strcmp(pdata->clients[i].name, dev_name(icd->control)))) break; dev_dbg(dev, "%s(%p): found #%d\n", __func__, dev, i); @@ -222,46 +280,8 @@ static int sh_csi2_client_connect(struct sh_csi2 *priv) if (i == pdata->num_clients) return -ENODEV; - /* Check if we can support this camera */ - csi2_flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_1_LANE; - - switch (pdata->type) { - case SH_CSI2C: - if (pdata->clients[i].lanes != 1) - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; - break; - case SH_CSI2I: - switch (pdata->clients[i].lanes) { - default: - csi2_flags |= V4L2_MBUS_CSI2_4_LANE; - case 3: - csi2_flags |= V4L2_MBUS_CSI2_3_LANE; - case 2: - csi2_flags |= V4L2_MBUS_CSI2_2_LANE; - } - } - - cfg.type = V4L2_MBUS_CSI2; - ret = v4l2_subdev_call(client_sd, video, g_mbus_config, &cfg); - if (ret == -ENOIOCTLCMD) - common_flags = csi2_flags; - else if (!ret) - common_flags = soc_mbus_config_compatible(&cfg, - csi2_flags); - else - common_flags = 0; - - if (!common_flags) - return -EINVAL; - - /* All good: camera MIPI configuration supported */ - priv->mipi_flags = common_flags; priv->client = pdata->clients + i; - pm_runtime_get_sync(dev); - - sh_csi2_hwinit(priv); - return 0; } @@ -304,11 +324,18 @@ static int sh_csi2_probe(struct platform_device *pdev) /* Platform data specify the PHY, lanes, ECC, CRC */ struct sh_csi2_pdata *pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); + if (!priv) + return -ENOMEM; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); /* Interrupt unused so far */ irq = platform_get_irq(pdev, 0); - if (!res || (int)irq <= 0 || !pdata) { + if (!res || (int)irq <= 0) { dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); return -ENODEV; } @@ -319,10 +346,6 @@ static int sh_csi2_probe(struct platform_device *pdev) return -EINVAL; } - priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_csi2), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->irq = irq; priv->base = devm_ioremap_resource(&pdev->dev, res); @@ -330,37 +353,35 @@ static int sh_csi2_probe(struct platform_device *pdev) return PTR_ERR(priv->base); priv->pdev = pdev; - platform_set_drvdata(pdev, priv); + priv->subdev.owner = THIS_MODULE; + priv->subdev.dev = &pdev->dev; + platform_set_drvdata(pdev, &priv->subdev); v4l2_subdev_init(&priv->subdev, &sh_csi2_subdev_ops); v4l2_set_subdevdata(&priv->subdev, &pdev->dev); snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.mipi-csi", - dev_name(pdata->v4l2_dev->dev)); - ret = v4l2_device_register_subdev(pdata->v4l2_dev, &priv->subdev); - dev_dbg(&pdev->dev, "%s(%p): ret(register_subdev) = %d\n", __func__, priv, ret); + dev_name(&pdev->dev)); + + ret = v4l2_async_register_subdev(&priv->subdev); if (ret < 0) - goto esdreg; + return ret; pm_runtime_enable(&pdev->dev); dev_dbg(&pdev->dev, "CSI2 probed.\n"); return 0; - -esdreg: - platform_set_drvdata(pdev, NULL); - - return ret; } static int sh_csi2_remove(struct platform_device *pdev) { - struct sh_csi2 *priv = platform_get_drvdata(pdev); + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct sh_csi2 *priv = container_of(subdev, struct sh_csi2, subdev); - v4l2_device_unregister_subdev(&priv->subdev); + v4l2_async_unregister_subdev(&priv->subdev); + v4l2_device_unregister_subdev(subdev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 3a4efbdc766..2dd0e527294 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -21,21 +21,23 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/list.h> -#include <linux/mutex.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> -#include <linux/pm_runtime.h> #include <linux/vmalloc.h> #include <media/soc_camera.h> +#include <media/soc_mediabus.h> +#include <media/v4l2-async.h> +#include <media/v4l2-clk.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-dev.h> #include <media/videobuf-core.h> #include <media/videobuf2-core.h> -#include <media/soc_mediabus.h> /* Default to VGA resolution */ #define DEFAULT_WIDTH 640 @@ -46,17 +48,39 @@ (icd)->vb_vidq.streaming : \ vb2_is_streaming(&(icd)->vb2_vidq)) +#define MAP_MAX_NUM 32 +static DECLARE_BITMAP(device_map, MAP_MAX_NUM); static LIST_HEAD(hosts); static LIST_HEAD(devices); -static DEFINE_MUTEX(list_lock); /* Protects the list of hosts */ +/* + * Protects lists and bitmaps of hosts and devices. + * Lock nesting: Ok to take ->host_lock under list_lock. + */ +static DEFINE_MUTEX(list_lock); + +struct soc_camera_async_client { + struct v4l2_async_subdev *sensor; + struct v4l2_async_notifier notifier; + struct platform_device *pdev; + struct list_head list; /* needed for clean up */ +}; + +static int soc_camera_video_start(struct soc_camera_device *icd); +static int video_dev_create(struct soc_camera_device *icd); -int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) +int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) { - int ret = regulator_bulk_enable(ssdd->num_regulators, + int ret = clk ? v4l2_clk_enable(clk) : 0; + if (ret < 0) { + dev_err(dev, "Cannot enable clock: %d\n", ret); + return ret; + } + ret = regulator_bulk_enable(ssdd->num_regulators, ssdd->regulators); if (ret < 0) { dev_err(dev, "Cannot enable regulators\n"); - return ret; + goto eregenable; } if (ssdd->power) { @@ -64,16 +88,25 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) if (ret < 0) { dev_err(dev, "Platform failed to power-on the camera.\n"); - regulator_bulk_disable(ssdd->num_regulators, - ssdd->regulators); + goto epwron; } } + return 0; + +epwron: + regulator_bulk_disable(ssdd->num_regulators, + ssdd->regulators); +eregenable: + if (clk) + v4l2_clk_disable(clk); + return ret; } EXPORT_SYMBOL(soc_camera_power_on); -int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd) +int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, + struct v4l2_clk *clk) { int ret = 0; int err; @@ -94,10 +127,21 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd ret = ret ? : err; } + if (clk) + v4l2_clk_disable(clk); + return ret; } EXPORT_SYMBOL(soc_camera_power_off); +int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd) +{ + + return devm_regulator_bulk_get(dev, ssdd->num_regulators, + ssdd->regulators); +} +EXPORT_SYMBOL(soc_camera_power_init); + static int __soc_camera_power_on(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -235,7 +279,6 @@ static int soc_camera_enum_input(struct file *file, void *priv, /* default is camera */ inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = V4L2_STD_UNKNOWN; strcpy(inp->name, "Camera"); return 0; @@ -505,6 +548,58 @@ static int soc_camera_set_fmt(struct soc_camera_device *icd, return ici->ops->set_bus_param(icd); } +static int soc_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + if (ici->icd) + return -EBUSY; + + if (!icd->clk) { + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + if (ret < 0) + return ret; + } + + if (ici->ops->add) { + ret = ici->ops->add(icd); + if (ret < 0) + goto eadd; + } + + ici->icd = icd; + + return 0; + +eadd: + if (!icd->clk) { + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); + } + return ret; +} + +static void soc_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + if (WARN_ON(icd != ici->icd)) + return; + + if (ici->ops->remove) + ici->ops->remove(icd); + if (!icd->clk) { + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); + } + ici->icd = NULL; +} + static int soc_camera_open(struct file *file) { struct video_device *vdev = video_devdata(file); @@ -525,7 +620,7 @@ static int soc_camera_open(struct file *file) return -ENODEV; } - icd = dev_get_drvdata(vdev->parent); + icd = video_get_drvdata(vdev); ici = to_soc_camera_host(icd->parent); ret = try_module_get(ici->ops->owner) ? 0 : -ENODEV; @@ -568,7 +663,7 @@ static int soc_camera_open(struct file *file) if (sdesc->subdev_desc.reset) sdesc->subdev_desc.reset(icd->pdev); - ret = ici->ops->add(icd); + ret = soc_camera_add_device(icd); if (ret < 0) { dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; @@ -610,8 +705,8 @@ static int soc_camera_open(struct file *file) return 0; /* - * First four errors are entered with the .host_lock held - * and use_count == 1 + * All errors are entered with the .host_lock held, first four also + * with use_count == 1 */ einitvb: esfmt: @@ -619,7 +714,7 @@ esfmt: eresume: __soc_camera_power_off(icd); epower: - ici->ops->remove(icd); + soc_camera_remove_device(icd); eiciadd: icd->use_count--; mutex_unlock(&ici->host_lock); @@ -645,7 +740,7 @@ static int soc_camera_close(struct file *file) vb2_queue_release(&icd->vb2_vidq); __soc_camera_power_off(icd); - ici->ops->remove(icd); + soc_camera_remove_device(icd); } if (icd->streamer == file) @@ -1036,76 +1131,225 @@ static int soc_camera_s_parm(struct file *file, void *fh, return -ENOIOCTLCMD; } -static int soc_camera_g_chip_ident(struct file *file, void *fh, - struct v4l2_dbg_chip_ident *id) +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd); + +/* So far this function cannot fail */ +static void scan_add_host(struct soc_camera_host *ici) { - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_device *icd; + + mutex_lock(&list_lock); + + list_for_each_entry(icd, &devices, list) + if (icd->iface == ici->nr) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; + + /* The camera could have been already on, try to reset */ + if (ssdd->reset) + ssdd->reset(icd->pdev); - return v4l2_subdev_call(sd, core, g_chip_ident, id); + icd->parent = ici->v4l2_dev.dev; + + /* Ignore errors */ + soc_camera_probe(ici, icd); + } + + mutex_unlock(&list_lock); } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int soc_camera_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) +/* + * It is invalid to call v4l2_clk_enable() after a successful probing + * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. + */ +static int soc_camera_clk_enable(struct v4l2_clk *clk) { - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + int ret; + + if (!icd || !icd->parent) + return -ENODEV; + + ici = to_soc_camera_host(icd->parent); + + if (!try_module_get(ici->ops->owner)) + return -ENODEV; - return v4l2_subdev_call(sd, core, g_register, reg); + /* + * If a different client is currently being probed, the host will tell + * you to go + */ + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + return ret; } -static int soc_camera_s_register(struct file *file, void *fh, - const struct v4l2_dbg_register *reg) +static void soc_camera_clk_disable(struct v4l2_clk *clk) { - struct soc_camera_device *icd = file->private_data; - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct soc_camera_device *icd = clk->priv; + struct soc_camera_host *ici; + + if (!icd || !icd->parent) + return; + + ici = to_soc_camera_host(icd->parent); + + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); - return v4l2_subdev_call(sd, core, s_register, reg); + module_put(ici->ops->owner); } -#endif -static int soc_camera_probe(struct soc_camera_device *icd); +/* + * Eventually, it would be more logical to make the respective host the clock + * owner, but then we would have to copy this struct for each ici. Besides, it + * would introduce the circular dependency problem, unless we port all client + * drivers to release the clock, when not in use. + */ +static const struct v4l2_clk_ops soc_camera_clk_ops = { + .owner = THIS_MODULE, + .enable = soc_camera_clk_enable, + .disable = soc_camera_clk_disable, +}; -/* So far this function cannot fail */ -static void scan_add_host(struct soc_camera_host *ici) +static int soc_camera_dyn_pdev(struct soc_camera_desc *sdesc, + struct soc_camera_async_client *sasc) { - struct soc_camera_device *icd; + struct platform_device *pdev; + int ret, i; mutex_lock(&list_lock); + i = find_first_zero_bit(device_map, MAP_MAX_NUM); + if (i < MAP_MAX_NUM) + set_bit(i, device_map); + mutex_unlock(&list_lock); + if (i >= MAP_MAX_NUM) + return -ENOMEM; - list_for_each_entry(icd, &devices, list) { - if (icd->iface == ici->nr) { - icd->parent = ici->v4l2_dev.dev; - soc_camera_probe(icd); - } + pdev = platform_device_alloc("soc-camera-pdrv", i); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); + if (ret < 0) { + platform_device_put(pdev); + return ret; } - mutex_unlock(&list_lock); + sasc->pdev = pdev; + + return 0; +} + +static struct soc_camera_device *soc_camera_add_pdev(struct soc_camera_async_client *sasc) +{ + struct platform_device *pdev = sasc->pdev; + int ret; + + ret = platform_device_add(pdev); + if (ret < 0 || !pdev->dev.driver) + return NULL; + + return platform_get_drvdata(pdev); +} + +/* Locking: called with .host_lock held */ +static int soc_camera_probe_finish(struct soc_camera_device *icd) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_framefmt mf; + int ret; + + sd->grp_id = soc_camera_grp_id(icd); + v4l2_set_subdev_hostdata(sd, icd); + + ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL); + if (ret < 0) + return ret; + + ret = soc_camera_add_device(icd); + if (ret < 0) { + dev_err(icd->pdev, "Couldn't activate the camera: %d\n", ret); + return ret; + } + + /* At this point client .probe() should have run already */ + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eusrfmt; + + icd->field = V4L2_FIELD_ANY; + + ret = soc_camera_video_start(icd); + if (ret < 0) + goto evidstart; + + /* Try to improve our guess of a reasonable window format */ + if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) { + icd->user_width = mf.width; + icd->user_height = mf.height; + icd->colorspace = mf.colorspace; + icd->field = mf.field; + } + soc_camera_remove_device(icd); + + return 0; + +evidstart: + soc_camera_free_user_formats(icd); +eusrfmt: + soc_camera_remove_device(icd); + + return ret; } #ifdef CONFIG_I2C_BOARDINFO -static int soc_camera_init_i2c(struct soc_camera_device *icd, +static int soc_camera_i2c_init(struct soc_camera_device *icd, struct soc_camera_desc *sdesc) { struct i2c_client *client; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct soc_camera_host *ici; struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id); + struct i2c_adapter *adap; struct v4l2_subdev *subdev; + char clk_name[V4L2_SUBDEV_NAME_SIZE]; + int ret; + /* First find out how we link the main client */ + if (icd->sasc) { + /* Async non-OF probing handled by the subdevice list */ + return -EPROBE_DEFER; + } + + ici = to_soc_camera_host(icd->parent); + adap = i2c_get_adapter(shd->i2c_adapter_id); if (!adap) { dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", shd->i2c_adapter_id); - goto ei2cga; + return -ENODEV; } shd->board_info->platform_data = &sdesc->subdev_desc; + snprintf(clk_name, sizeof(clk_name), "%d-%04x", + shd->i2c_adapter_id, shd->board_info->addr); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, shd->board_info, NULL); - if (!subdev) + if (!subdev) { + ret = -ENODEV; goto ei2cnd; + } client = v4l2_get_subdevdata(subdev); @@ -1114,39 +1358,203 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, return 0; ei2cnd: + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; i2c_put_adapter(adap); -ei2cga: - return -ENODEV; + return ret; } -static void soc_camera_free_i2c(struct soc_camera_device *icd) +static void soc_camera_i2c_free(struct soc_camera_device *icd) { struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - struct i2c_adapter *adap = client->adapter; + struct i2c_adapter *adap; icd->control = NULL; + if (icd->sasc) + return; + + adap = client->adapter; v4l2_device_unregister_subdev(i2c_get_clientdata(client)); i2c_unregister_device(client); i2c_put_adapter(adap); + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; +} + +/* + * V4L2 asynchronous notifier callbacks. They are all called under a v4l2-async + * internal global mutex, therefore cannot race against other asynchronous + * events. Until notifier->complete() (soc_camera_async_complete()) is called, + * the video device node is not registered and no V4L fops can occur. Unloading + * of the host driver also calls a v4l2-async function, so also there we're + * protected. + */ +static int soc_camera_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (asd == sasc->sensor && !WARN_ON(icd->control)) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* + * Only now we get subdevice-specific information like + * regulators, flags, callbacks, etc. + */ + if (client) { + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_subdev_desc *ssdd = + soc_camera_i2c_to_desc(client); + if (ssdd) { + memcpy(&sdesc->subdev_desc, ssdd, + sizeof(sdesc->subdev_desc)); + if (ssdd->reset) + ssdd->reset(icd->pdev); + } + + icd->control = &client->dev; + } + } + + return 0; +} + +static void soc_camera_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (icd->clk) { + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } +} + +static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) +{ + struct soc_camera_async_client *sasc = container_of(notifier, + struct soc_camera_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (to_soc_camera_control(icd)) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + mutex_lock(&list_lock); + ret = soc_camera_probe(ici, icd); + mutex_unlock(&list_lock); + if (ret < 0) + return ret; + } + + return 0; +} + +static int scan_async_group(struct soc_camera_host *ici, + struct v4l2_async_subdev **asd, unsigned int size) +{ + struct soc_camera_async_subdev *sasd; + struct soc_camera_async_client *sasc; + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + char clk_name[V4L2_SUBDEV_NAME_SIZE]; + int ret, i; + + /* First look for a sensor */ + for (i = 0; i < size; i++) { + sasd = container_of(asd[i], struct soc_camera_async_subdev, asd); + if (sasd->role == SOCAM_SUBDEV_DATA_SOURCE) + break; + } + + if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) { + /* All useless */ + dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n"); + return -ENODEV; + } + + /* Or shall this be managed by the soc-camera device? */ + sasc = devm_kzalloc(ici->v4l2_dev.dev, sizeof(*sasc), GFP_KERNEL); + if (!sasc) + return -ENOMEM; + + /* HACK: just need a != NULL */ + sdesc.host_desc.board_info = ERR_PTR(-ENODATA); + + ret = soc_camera_dyn_pdev(&sdesc, sasc); + if (ret < 0) + return ret; + + sasc->sensor = &sasd->asd; + + icd = soc_camera_add_pdev(sasc); + if (!icd) { + platform_device_put(sasc->pdev); + return -ENOMEM; + } + + sasc->notifier.subdev = asd; + sasc->notifier.num_subdevs = size; + sasc->notifier.bound = soc_camera_async_bound; + sasc->notifier.unbind = soc_camera_async_unbind; + sasc->notifier.complete = soc_camera_async_complete; + + icd->sasc = sasc; + icd->parent = ici->v4l2_dev.dev; + + snprintf(clk_name, sizeof(clk_name), "%d-%04x", + sasd->asd.match.i2c.adapter_id, sasd->asd.match.i2c.address); + + icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); + if (IS_ERR(icd->clk)) { + ret = PTR_ERR(icd->clk); + goto eclkreg; + } + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + v4l2_clk_unregister(icd->clk); +eclkreg: + icd->clk = NULL; + platform_device_unregister(sasc->pdev); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static void scan_async_host(struct soc_camera_host *ici) +{ + struct v4l2_async_subdev **asd; + int j; + + for (j = 0, asd = ici->asd; ici->asd_sizes[j]; j++) { + scan_async_group(ici, asd, ici->asd_sizes[j]); + asd += ici->asd_sizes[j]; + } } #else -#define soc_camera_init_i2c(icd, sdesc) (-ENODEV) -#define soc_camera_free_i2c(icd) do {} while (0) +#define soc_camera_i2c_init(icd, sdesc) (-ENODEV) +#define soc_camera_i2c_free(icd) do {} while (0) +#define scan_async_host(ici) do {} while (0) #endif -static int soc_camera_video_start(struct soc_camera_device *icd); -static int video_dev_create(struct soc_camera_device *icd); /* Called during host-driver probe */ -static int soc_camera_probe(struct soc_camera_device *icd) +static int soc_camera_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd) { - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct soc_camera_host_desc *shd = &sdesc->host_desc; - struct soc_camera_subdev_desc *ssdd = &sdesc->subdev_desc; struct device *control = NULL; - struct v4l2_subdev *sd; - struct v4l2_mbus_framefmt mf; int ret; dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); @@ -1162,30 +1570,32 @@ static int soc_camera_probe(struct soc_camera_device *icd) if (ret < 0) return ret; - /* The camera could have been already on, try to reset */ - if (ssdd->reset) - ssdd->reset(icd->pdev); - - mutex_lock(&ici->host_lock); - ret = ici->ops->add(icd); - mutex_unlock(&ici->host_lock); - if (ret < 0) - goto eadd; - /* Must have icd->vdev before registering the device */ ret = video_dev_create(icd); if (ret < 0) goto evdc; + /* + * ..._video_start() will create a device node, video_register_device() + * itself is protected against concurrent open() calls, but we also have + * to protect our data also during client probing. + */ + /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ if (shd->board_info) { - ret = soc_camera_init_i2c(icd, sdesc); - if (ret < 0) - goto eadddev; + ret = soc_camera_i2c_init(icd, sdesc); + if (ret < 0 && ret != -EPROBE_DEFER) + goto eadd; } else if (!shd->add_device || !shd->del_device) { ret = -EINVAL; - goto eadddev; + goto eadd; } else { + mutex_lock(&ici->clk_lock); + ret = ici->ops->clock_start(ici); + mutex_unlock(&ici->clk_lock); + if (ret < 0) + goto eadd; + if (shd->module_name) ret = request_module(shd->module_name); @@ -1206,81 +1616,49 @@ static int soc_camera_probe(struct soc_camera_device *icd) } } - sd = soc_camera_to_subdev(icd); - sd->grp_id = soc_camera_grp_id(icd); - v4l2_set_subdev_hostdata(sd, icd); - - ret = v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler, NULL); - if (ret < 0) - goto ectrl; - - /* At this point client .probe() should have run already */ - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eiufmt; - - icd->field = V4L2_FIELD_ANY; - - /* - * ..._video_start() will create a device node, video_register_device() - * itself is protected against concurrent open() calls, but we also have - * to protect our data. - */ mutex_lock(&ici->host_lock); - - ret = soc_camera_video_start(icd); - if (ret < 0) - goto evidstart; - - /* Try to improve our guess of a reasonable window format */ - if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) { - icd->user_width = mf.width; - icd->user_height = mf.height; - icd->colorspace = mf.colorspace; - icd->field = mf.field; - } - - ici->ops->remove(icd); - + ret = soc_camera_probe_finish(icd); mutex_unlock(&ici->host_lock); + if (ret < 0) + goto efinish; return 0; -evidstart: - mutex_unlock(&ici->host_lock); - soc_camera_free_user_formats(icd); -eiufmt: -ectrl: +efinish: if (shd->board_info) { - soc_camera_free_i2c(icd); + soc_camera_i2c_free(icd); } else { shd->del_device(icd); module_put(control->driver->owner); - } enodrv: eadddev: + mutex_lock(&ici->clk_lock); + ici->ops->clock_stop(ici); + mutex_unlock(&ici->clk_lock); + } +eadd: video_device_release(icd->vdev); icd->vdev = NULL; + if (icd->vdev) { + video_device_release(icd->vdev); + icd->vdev = NULL; + } evdc: - mutex_lock(&ici->host_lock); - ici->ops->remove(icd); - mutex_unlock(&ici->host_lock); -eadd: v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } /* * This is called on device_unregister, which only means we have to disconnect - * from the host, but not remove ourselves from the device list + * from the host, but not remove ourselves from the device list. With + * asynchronous client probing this can also be called without + * soc_camera_probe_finish() having run. Careful with clean up. */ static int soc_camera_remove(struct soc_camera_device *icd) { struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); struct video_device *vdev = icd->vdev; - BUG_ON(!icd->parent); - v4l2_ctrl_handler_free(&icd->ctrl_handler); if (vdev) { video_unregister_device(vdev); @@ -1288,15 +1666,27 @@ static int soc_camera_remove(struct soc_camera_device *icd) } if (sdesc->host_desc.board_info) { - soc_camera_free_i2c(icd); + soc_camera_i2c_free(icd); } else { - struct device_driver *drv = to_soc_camera_control(icd)->driver; + struct device *dev = to_soc_camera_control(icd); + struct device_driver *drv = dev ? dev->driver : NULL; if (drv) { sdesc->host_desc.del_device(icd); module_put(drv->owner); } } - soc_camera_free_user_formats(icd); + + if (icd->num_user_formats) + soc_camera_free_user_formats(icd); + + if (icd->clk) { + /* For the synchronous case */ + v4l2_clk_unregister(icd->clk); + icd->clk = NULL; + } + + if (icd->sasc) + platform_device_unregister(icd->sasc->pdev); return 0; } @@ -1372,8 +1762,8 @@ int soc_camera_host_register(struct soc_camera_host *ici) ((!ici->ops->init_videobuf || !ici->ops->reqbufs) && !ici->ops->init_videobuf2) || - !ici->ops->add || - !ici->ops->remove || + !ici->ops->clock_start || + !ici->ops->clock_stop || !ici->ops->poll || !ici->v4l2_dev.dev) return -EINVAL; @@ -1407,7 +1797,18 @@ int soc_camera_host_register(struct soc_camera_host *ici) mutex_unlock(&list_lock); mutex_init(&ici->host_lock); - scan_add_host(ici); + mutex_init(&ici->clk_lock); + + if (ici->asd_sizes) + /* + * No OF, host with a list of subdevices. Don't try to mix + * modes by initialising some groups statically and some + * dynamically! + */ + scan_async_host(ici); + else + /* Legacy: static platform devices from board data */ + scan_add_host(ici); return 0; @@ -1420,13 +1821,30 @@ EXPORT_SYMBOL(soc_camera_host_register); /* Unregister all clients! */ void soc_camera_host_unregister(struct soc_camera_host *ici) { - struct soc_camera_device *icd; + struct soc_camera_device *icd, *tmp; + struct soc_camera_async_client *sasc; + LIST_HEAD(notifiers); mutex_lock(&list_lock); - list_del(&ici->list); list_for_each_entry(icd, &devices, list) - if (icd->iface == ici->nr && to_soc_camera_control(icd)) + if (icd->iface == ici->nr && icd->sasc) { + /* as long as we hold the device, sasc won't be freed */ + get_device(icd->pdev); + list_add(&icd->sasc->list, ¬ifiers); + } + mutex_unlock(&list_lock); + + list_for_each_entry(sasc, ¬ifiers, list) { + /* Must call unlocked to avoid AB-BA dead-lock */ + v4l2_async_notifier_unregister(&sasc->notifier); + put_device(&sasc->pdev->dev); + } + + mutex_lock(&list_lock); + + list_for_each_entry_safe(icd, tmp, &devices, list) + if (icd->iface == ici->nr) soc_camera_remove(icd); mutex_unlock(&list_lock); @@ -1441,6 +1859,7 @@ static int soc_camera_device_register(struct soc_camera_device *icd) struct soc_camera_device *ix; int num = -1, i; + mutex_lock(&list_lock); for (i = 0; i < 256 && num < 0; i++) { num = i; /* Check if this index is available on this interface */ @@ -1452,18 +1871,34 @@ static int soc_camera_device_register(struct soc_camera_device *icd) } } - if (num < 0) + if (num < 0) { /* * ok, we have 256 cameras on this host... * man, stay reasonable... */ + mutex_unlock(&list_lock); return -ENOMEM; + } icd->devnum = num; icd->use_count = 0; icd->host_priv = NULL; + /* + * Dynamically allocated devices set the bit earlier, but it doesn't hurt setting + * it again + */ + i = to_platform_device(icd->pdev)->id; + if (i < 0) + /* One static (legacy) soc-camera platform device */ + i = 0; + if (i >= MAP_MAX_NUM) { + mutex_unlock(&list_lock); + return -EBUSY; + } + set_bit(i, device_map); list_add_tail(&icd->list, &devices); + mutex_unlock(&list_lock); return 0; } @@ -1495,11 +1930,6 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_s_selection = soc_camera_s_selection, .vidioc_g_parm = soc_camera_g_parm, .vidioc_s_parm = soc_camera_s_parm, - .vidioc_g_chip_ident = soc_camera_g_chip_ident, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = soc_camera_g_register, - .vidioc_s_register = soc_camera_s_register, -#endif }; static int video_dev_create(struct soc_camera_device *icd) @@ -1512,12 +1942,10 @@ static int video_dev_create(struct soc_camera_device *icd) strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); - vdev->parent = icd->pdev; - vdev->current_norm = V4L2_STD_UNKNOWN; + vdev->v4l2_dev = &ici->v4l2_dev; vdev->fops = &soc_camera_fops; vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; - vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &ici->host_lock; @@ -1537,6 +1965,7 @@ static int soc_camera_video_start(struct soc_camera_device *icd) if (!icd->parent) return -ENODEV; + video_set_drvdata(icd->vdev, icd); ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { dev_err(icd->pdev, "video_register_device failed: %d\n", ret); @@ -1563,6 +1992,12 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev) if (!icd) return -ENOMEM; + /* + * In the asynchronous case ssdd->num_regulators == 0 yet, so, the below + * regulator allocation is a dummy. They will be really requested later + * in soc_camera_async_bind(). Also note, that in that case regulators + * are attached to the I2C device and not to the camera platform device. + */ ret = devm_regulator_bulk_get(&pdev->dev, ssdd->num_regulators, ssdd->regulators); if (ret < 0) @@ -1587,11 +2022,25 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev) static int soc_camera_pdrv_remove(struct platform_device *pdev) { struct soc_camera_device *icd = platform_get_drvdata(pdev); + int i; if (!icd) return -EINVAL; - list_del(&icd->list); + i = pdev->id; + if (i < 0) + i = 0; + + /* + * In synchronous mode with static platform devices this is called in a + * loop from drivers/base/dd.c::driver_detach(), no parallel execution, + * no need to lock. In asynchronous case the caller - + * soc_camera_host_unregister() - already holds the lock + */ + if (test_bit(i, device_map)) { + clear_bit(i, device_map); + list_del(&icd->list); + } return 0; } diff --git a/drivers/media/platform/soc_camera/soc_camera_platform.c b/drivers/media/platform/soc_camera/soc_camera_platform.c index 1b7a88ca195..ceaddfb85e4 100644 --- a/drivers/media/platform/soc_camera/soc_camera_platform.c +++ b/drivers/media/platform/soc_camera/soc_camera_platform.c @@ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); - return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on); + return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on); } static struct v4l2_subdev_core_ops platform_subdev_core_ops = { @@ -137,7 +137,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) struct soc_camera_platform_priv *priv; struct soc_camera_platform_info *p = pdev->dev.platform_data; struct soc_camera_device *icd; - int ret; if (!p) return -EINVAL; @@ -165,15 +164,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) v4l2_set_subdevdata(&priv->subdev, p); strncpy(priv->subdev.name, dev_name(&pdev->dev), V4L2_SUBDEV_NAME_SIZE); - ret = v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); - if (ret) - goto evdrs; - - return ret; - -evdrs: - platform_set_drvdata(pdev, NULL); - return ret; + return v4l2_device_register_subdev(&ici->v4l2_dev, &priv->subdev); } static int soc_camera_platform_remove(struct platform_device *pdev) @@ -183,7 +174,6 @@ static int soc_camera_platform_remove(struct platform_device *pdev) p->icd->control = NULL; v4l2_device_unregister_subdev(&priv->subdev); - platform_set_drvdata(pdev, NULL); return 0; } diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.c b/drivers/media/platform/soc_camera/soc_scale_crop.c new file mode 100644 index 00000000000..cbd3a34f4f3 --- /dev/null +++ b/drivers/media/platform/soc_camera/soc_scale_crop.c @@ -0,0 +1,402 @@ +/* + * soc-camera generic scaling-cropping manipulation functions + * + * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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. + */ + +#include <linux/device.h> +#include <linux/module.h> + +#include <media/soc_camera.h> +#include <media/v4l2-common.h> + +#include "soc_scale_crop.h" + +#ifdef DEBUG_GEOMETRY +#define dev_geo dev_info +#else +#define dev_geo dev_dbg +#endif + +/* Check if any dimension of r1 is smaller than respective one of r2 */ +static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) +{ + return r1->width < r2->width || r1->height < r2->height; +} + +/* Check if r1 fails to cover r2 */ +static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) +{ + return r1->left > r2->left || r1->top > r2->top || + r1->left + r1->width < r2->left + r2->width || + r1->top + r1->height < r2->top + r2->height; +} + +/* Get and store current client crop */ +int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) +{ + struct v4l2_crop crop; + struct v4l2_cropcap cap; + int ret; + + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, g_crop, &crop); + if (!ret) { + *rect = crop.c; + return ret; + } + + /* Camera driver doesn't support .g_crop(), assume default rectangle */ + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (!ret) + *rect = cap.defrect; + + return ret; +} +EXPORT_SYMBOL(soc_camera_client_g_rect); + +/* Client crop has changed, update our sub-rectangle to remain within the area */ +static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect) +{ + if (rect->width < subrect->width) + subrect->width = rect->width; + + if (rect->height < subrect->height) + subrect->height = rect->height; + + if (rect->left > subrect->left) + subrect->left = rect->left; + else if (rect->left + rect->width > + subrect->left + subrect->width) + subrect->left = rect->left + rect->width - + subrect->width; + + if (rect->top > subrect->top) + subrect->top = rect->top; + else if (rect->top + rect->height > + subrect->top + subrect->height) + subrect->top = rect->top + rect->height - + subrect->height; +} + +/* + * The common for both scaling and cropping iterative approach is: + * 1. try if the client can produce exactly what requested by the user + * 2. if (1) failed, try to double the client image until we get one big enough + * 3. if (2) failed, try to request the maximum image + */ +int soc_camera_client_s_crop(struct v4l2_subdev *sd, + struct v4l2_crop *crop, struct v4l2_crop *cam_crop, + struct v4l2_rect *target_rect, struct v4l2_rect *subrect) +{ + struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; + struct device *dev = sd->v4l2_dev->dev; + struct v4l2_cropcap cap; + int ret; + unsigned int width, height; + + v4l2_subdev_call(sd, video, s_crop, crop); + ret = soc_camera_client_g_rect(sd, cam_rect); + if (ret < 0) + return ret; + + /* + * Now cam_crop contains the current camera input rectangle, and it must + * be within camera cropcap bounds + */ + if (!memcmp(rect, cam_rect, sizeof(*rect))) { + /* Even if camera S_CROP failed, but camera rectangle matches */ + dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", + rect->width, rect->height, rect->left, rect->top); + *target_rect = *cam_rect; + return 0; + } + + /* Try to fix cropping, that camera hasn't managed to set */ + dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top, + rect->width, rect->height, rect->left, rect->top); + + /* We need sensor maximum rectangle */ + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + /* Put user requested rectangle within sensor bounds */ + soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, + cap.bounds.width); + soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, + cap.bounds.height); + + /* + * Popular special case - some cameras can only handle fixed sizes like + * QVGA, VGA,... Take care to avoid infinite loop. + */ + width = max(cam_rect->width, 2); + height = max(cam_rect->height, 2); + + /* + * Loop as long as sensor is not covering the requested rectangle and + * is still within its bounds + */ + while (!ret && (is_smaller(cam_rect, rect) || + is_inside(cam_rect, rect)) && + (cap.bounds.width > width || cap.bounds.height > height)) { + + width *= 2; + height *= 2; + + cam_rect->width = width; + cam_rect->height = height; + + /* + * We do not know what capabilities the camera has to set up + * left and top borders. We could try to be smarter in iterating + * them, e.g., if camera current left is to the right of the + * target left, set it to the middle point between the current + * left and minimum left. But that would add too much + * complexity: we would have to iterate each border separately. + * Instead we just drop to the left and top bounds. + */ + if (cam_rect->left > rect->left) + cam_rect->left = cap.bounds.left; + + if (cam_rect->left + cam_rect->width < rect->left + rect->width) + cam_rect->width = rect->left + rect->width - + cam_rect->left; + + if (cam_rect->top > rect->top) + cam_rect->top = cap.bounds.top; + + if (cam_rect->top + cam_rect->height < rect->top + rect->height) + cam_rect->height = rect->top + rect->height - + cam_rect->top; + + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = soc_camera_client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + /* S_CROP must not modify the rectangle */ + if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { + /* + * The camera failed to configure a suitable cropping, + * we cannot use the current rectangle, set to max + */ + *cam_rect = cap.bounds; + v4l2_subdev_call(sd, video, s_crop, cam_crop); + ret = soc_camera_client_g_rect(sd, cam_rect); + dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + } + + if (!ret) { + *target_rect = *cam_rect; + update_subrect(target_rect, subrect); + } + + return ret; +} +EXPORT_SYMBOL(soc_camera_client_s_crop); + +/* Iterative s_mbus_fmt, also updates cached client crop on success */ +static int client_s_fmt(struct soc_camera_device *icd, + struct v4l2_rect *rect, struct v4l2_rect *subrect, + unsigned int max_width, unsigned int max_height, + struct v4l2_mbus_framefmt *mf, bool host_can_scale) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; + struct v4l2_cropcap cap; + bool host_1to1; + int ret; + + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), video, + s_mbus_fmt, mf); + if (ret < 0) + return ret; + + dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); + + if (width == mf->width && height == mf->height) { + /* Perfect! The client has done it all. */ + host_1to1 = true; + goto update_cache; + } + + host_1to1 = false; + if (!host_can_scale) + goto update_cache; + + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = v4l2_subdev_call(sd, video, cropcap, &cap); + if (ret < 0) + return ret; + + if (max_width > cap.bounds.width) + max_width = cap.bounds.width; + if (max_height > cap.bounds.height) + max_height = cap.bounds.height; + + /* Camera set a format, but geometry is not precise, try to improve */ + tmp_w = mf->width; + tmp_h = mf->height; + + /* width <= max_width && height <= max_height - guaranteed by try_fmt */ + while ((width > tmp_w || height > tmp_h) && + tmp_w < max_width && tmp_h < max_height) { + tmp_w = min(2 * tmp_w, max_width); + tmp_h = min(2 * tmp_h, max_height); + mf->width = tmp_w; + mf->height = tmp_h; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), video, + s_mbus_fmt, mf); + dev_geo(dev, "Camera scaled to %ux%u\n", + mf->width, mf->height); + if (ret < 0) { + /* This shouldn't happen */ + dev_err(dev, "Client failed to set format: %d\n", ret); + return ret; + } + } + +update_cache: + /* Update cache */ + ret = soc_camera_client_g_rect(sd, rect); + if (ret < 0) + return ret; + + if (host_1to1) + *subrect = *rect; + else + update_subrect(rect, subrect); + + return 0; +} + +/** + * @icd - soc-camera device + * @rect - camera cropping window + * @subrect - part of rect, sent to the user + * @mf - in- / output camera output window + * @width - on input: max host input width + * on output: user width, mapped back to input + * @height - on input: max host input height + * on output: user height, mapped back to input + * @host_can_scale - host can scale this pixel format + * @shift - shift, used for scaling + */ +int soc_camera_client_scale(struct soc_camera_device *icd, + struct v4l2_rect *rect, struct v4l2_rect *subrect, + struct v4l2_mbus_framefmt *mf, + unsigned int *width, unsigned int *height, + bool host_can_scale, unsigned int shift) +{ + struct device *dev = icd->parent; + struct v4l2_mbus_framefmt mf_tmp = *mf; + unsigned int scale_h, scale_v; + int ret; + + /* + * 5. Apply iterative camera S_FMT for camera user window (also updates + * client crop cache and the imaginary sub-rectangle). + */ + ret = client_s_fmt(icd, rect, subrect, *width, *height, + &mf_tmp, host_can_scale); + if (ret < 0) + return ret; + + dev_geo(dev, "5: camera scaled to %ux%u\n", + mf_tmp.width, mf_tmp.height); + + /* 6. Retrieve camera output window (g_fmt) */ + + /* unneeded - it is already in "mf_tmp" */ + + /* 7. Calculate new client scales. */ + scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width); + scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height); + + mf->width = mf_tmp.width; + mf->height = mf_tmp.height; + mf->colorspace = mf_tmp.colorspace; + + /* + * 8. Calculate new host crop - apply camera scales to previously + * updated "effective" crop. + */ + *width = soc_camera_shift_scale(subrect->width, shift, scale_h); + *height = soc_camera_shift_scale(subrect->height, shift, scale_v); + + dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); + + return 0; +} +EXPORT_SYMBOL(soc_camera_client_scale); + +/* + * Calculate real client output window by applying new scales to the current + * client crop. New scales are calculated from the requested output format and + * host crop, mapped backed onto the client input (subrect). + */ +void soc_camera_calc_client_output(struct soc_camera_device *icd, + struct v4l2_rect *rect, struct v4l2_rect *subrect, + const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, + unsigned int shift) +{ + struct device *dev = icd->parent; + unsigned int scale_v, scale_h; + + if (subrect->width == rect->width && + subrect->height == rect->height) { + /* No sub-cropping */ + mf->width = pix->width; + mf->height = pix->height; + return; + } + + /* 1.-2. Current camera scales and subwin - cached. */ + + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", + subrect->width, subrect->height, + subrect->left, subrect->top); + + /* + * 3. Calculate new combined scales from input sub-window to requested + * user window. + */ + + /* + * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF + * (128x96) or larger than VGA. This and similar limitations have to be + * taken into account here. + */ + scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width); + scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height); + + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); + + /* + * 4. Calculate desired client output window by applying combined scales + * to client (real) input window. + */ + mf->width = soc_camera_shift_scale(rect->width, shift, scale_h); + mf->height = soc_camera_shift_scale(rect->height, shift, scale_v); +} +EXPORT_SYMBOL(soc_camera_calc_client_output); diff --git a/drivers/media/platform/soc_camera/soc_scale_crop.h b/drivers/media/platform/soc_camera/soc_scale_crop.h new file mode 100644 index 00000000000..184a30dff54 --- /dev/null +++ b/drivers/media/platform/soc_camera/soc_scale_crop.h @@ -0,0 +1,47 @@ +/* + * soc-camera generic scaling-cropping manipulation functions + * + * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * 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. + */ + +#ifndef SOC_SCALE_CROP_H +#define SOC_SCALE_CROP_H + +#include <linux/kernel.h> + +struct soc_camera_device; + +struct v4l2_crop; +struct v4l2_mbus_framefmt; +struct v4l2_pix_format; +struct v4l2_rect; +struct v4l2_subdev; + +static inline unsigned int soc_camera_shift_scale(unsigned int size, + unsigned int shift, unsigned int scale) +{ + return DIV_ROUND_CLOSEST(size << shift, scale); +} + +#define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out) + +int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); +int soc_camera_client_s_crop(struct v4l2_subdev *sd, + struct v4l2_crop *crop, struct v4l2_crop *cam_crop, + struct v4l2_rect *target_rect, struct v4l2_rect *subrect); +int soc_camera_client_scale(struct soc_camera_device *icd, + struct v4l2_rect *rect, struct v4l2_rect *subrect, + struct v4l2_mbus_framefmt *mf, + unsigned int *width, unsigned int *height, + bool host_can_scale, unsigned int shift); +void soc_camera_calc_client_output(struct soc_camera_device *icd, + struct v4l2_rect *rect, struct v4l2_rect *subrect, + const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, + unsigned int shift); + +#endif diff --git a/drivers/media/platform/timblogiw.c b/drivers/media/platform/timblogiw.c index a2f7bdd5104..b557caf5b1a 100644 --- a/drivers/media/platform/timblogiw.c +++ b/drivers/media/platform/timblogiw.c @@ -239,13 +239,12 @@ static int timblogiw_querycap(struct file *file, void *priv, struct video_device *vdev = video_devdata(file); dev_dbg(&vdev->dev, "%s: Entry\n", __func__); - memset(cap, 0, sizeof(*cap)); strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1); strncpy(cap->driver, DRIVER_NAME, sizeof(cap->driver) - 1); - strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info)); - cap->version = TIMBLOGIW_VERSION_CODE; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", vdev->name); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -834,11 +833,9 @@ static int timblogiw_probe(struct platform_device *pdev) goto err_request; } - return 0; err_request: - platform_set_drvdata(pdev, NULL); v4l2_device_unregister(&lw->v4l2_dev); err_register: kfree(lw); @@ -858,8 +855,6 @@ static int timblogiw_remove(struct platform_device *pdev) kfree(lw); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via-camera.c index a794cd6c444..b4f9d03636e 100644 --- a/drivers/media/platform/via-camera.c +++ b/drivers/media/platform/via-camera.c @@ -17,7 +17,6 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-chip-ident.h> #include <media/v4l2-ctrls.h> #include <media/ov7670.h> #include <media/videobuf-dma-sg.h> @@ -805,20 +804,6 @@ static const struct v4l2_file_operations viacam_fops = { * The long list of v4l2 ioctl ops */ -static int viacam_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *ident) -{ - struct via_camera *cam = priv; - - ident->ident = V4L2_IDENT_NONE; - ident->revision = 0; - if (v4l2_chip_match_host(&ident->match)) { - ident->ident = V4L2_IDENT_VIA_VX855; - return 0; - } - return sensor_call(cam, core, g_chip_ident, ident); -} - /* * Only one input. */ @@ -852,6 +837,12 @@ static int viacam_s_std(struct file *filp, void *priv, v4l2_std_id std) return 0; } +static int viacam_g_std(struct file *filp, void *priv, v4l2_std_id *std) +{ + *std = V4L2_STD_NTSC_M; + return 0; +} + /* * Video format stuff. Here is our default format until * user space messes with things. @@ -1174,11 +1165,11 @@ static int viacam_enum_frameintervals(struct file *filp, void *priv, static const struct v4l2_ioctl_ops viacam_ioctl_ops = { - .vidioc_g_chip_ident = viacam_g_chip_ident, .vidioc_enum_input = viacam_enum_input, .vidioc_g_input = viacam_g_input, .vidioc_s_input = viacam_s_input, .vidioc_s_std = viacam_s_std, + .vidioc_g_std = viacam_g_std, .vidioc_enum_fmt_vid_cap = viacam_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = viacam_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = viacam_g_fmt_vid_cap, @@ -1266,7 +1257,6 @@ static struct video_device viacam_v4l_template = { .name = "via-camera", .minor = -1, .tvnorms = V4L2_STD_NTSC_M, - .current_norm = V4L2_STD_NTSC_M, .fops = &viacam_fops, .ioctl_ops = &viacam_ioctl_ops, .release = video_device_release_empty, /* Check this */ |