aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig2
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c81
-rw-r--r--drivers/media/platform/blackfin/ppi.c12
-rw-r--r--drivers/media/platform/coda.c635
-rw-r--r--drivers/media/platform/coda.h11
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c31
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c24
-rw-r--r--drivers/media/platform/davinci/vpif.c45
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c160
-rw-r--r--drivers/media/platform/davinci/vpif_capture.h5
-rw-r--r--drivers/media/platform/davinci/vpif_display.c153
-rw-r--r--drivers/media/platform/davinci/vpif_display.h5
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c2
-rw-r--r--drivers/media/platform/exynos4-is/Kconfig5
-rw-r--r--drivers/media/platform/exynos4-is/Makefile5
-rw-r--r--drivers/media/platform/exynos4-is/common.c53
-rw-r--r--drivers/media/platform/exynos4-is/common.h16
-rw-r--r--drivers/media/platform/exynos4-is/fimc-capture.c412
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c11
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.h15
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c84
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c12
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.h12
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c130
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.h21
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.c55
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite-reg.h10
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c321
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.h35
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-reg.c7
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c272
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.h54
-rw-r--r--drivers/media/platform/exynos4-is/mipi-csis.c67
-rw-r--r--drivers/media/platform/fsl-viu.c2
-rw-r--r--drivers/media/platform/indycam.c12
-rw-r--r--drivers/media/platform/m2m-deinterlace.c1
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c4
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c67
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h9
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c4
-rw-r--r--drivers/media/platform/mem2mem_testdev.c3
-rw-r--r--drivers/media/platform/mx2_emmaprp.c1
-rw-r--r--drivers/media/platform/omap/omap_vout.c3
-rw-r--r--drivers/media/platform/omap24xxcam.c9
-rw-r--r--drivers/media/platform/omap24xxcam.h3
-rw-r--r--drivers/media/platform/omap3isp/isp.c51
-rw-r--r--drivers/media/platform/omap3isp/ispccdc.c2
-rw-r--r--drivers/media/platform/omap3isp/ispccp2.c21
-rw-r--r--drivers/media/platform/omap3isp/ispcsi2.c2
-rw-r--r--drivers/media/platform/omap3isp/ispqueue.h1
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c6
-rw-r--r--drivers/media/platform/s3c-camif/camif-capture.c2
-rw-r--r--drivers/media/platform/s3c-camif/camif-core.c6
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.c6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c2
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c39
-rw-r--r--drivers/media/platform/s5p-tv/mixer_drv.c22
-rw-r--r--drivers/media/platform/s5p-tv/mixer_video.c3
-rw-r--r--drivers/media/platform/s5p-tv/sdo_drv.c22
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c4
-rw-r--r--drivers/media/platform/sh_veu.c5
-rw-r--r--drivers/media/platform/sh_vou.c34
-rw-r--r--drivers/media/platform/soc_camera/Kconfig12
-rw-r--r--drivers/media/platform/soc_camera/Makefile6
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c38
-rw-r--r--drivers/media/platform/soc_camera/mx1_camera.c48
-rw-r--r--drivers/media/platform/soc_camera/mx2_camera.c41
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c44
-rw-r--r--drivers/media/platform/soc_camera/omap1_camera.c41
-rw-r--r--drivers/media/platform/soc_camera/pxa_camera.c46
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c642
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_csi2.c161
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c737
-rw-r--r--drivers/media/platform/soc_camera/soc_camera_platform.c14
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.c402
-rw-r--r--drivers/media/platform/soc_camera/soc_scale_crop.h47
-rw-r--r--drivers/media/platform/timblogiw.c11
-rw-r--r--drivers/media/platform/via-camera.c24
81 files changed, 3103 insertions, 2327 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 25eaf61b98b4..08de865cc399 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 0e55b087076f..7f838c681cea 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 01b5b501347e..15e9c2bac2b1 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(&reg->vcnt, params->height);
if (params->int_mask)
bfin_write32(&reg->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(&reg->fs1_wlhb, hsync_width);
+ bfin_write32(&reg->fs1_paspl, samples_per_line);
+ bfin_write32(&reg->fs2_wlvb, vsync_width);
+ bfin_write32(&reg->fs2_palpf, vsync_period);
+ }
break;
}
default:
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index 9d1481a60bd9..df4ada880e42 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 f3f5e43c1ac2..ace0bf0a3b9c 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 d0b375cf565f..e180ff7282d9 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 = &reg->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 396a51cbede7..6ed82e8b297b 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 ea82a8bd2803..cd08e5248387 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 5f98df1fc8a0..5514175bbd07 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 3d3c1e5cd5d4..0ebb31260369 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 1b3fb5ca2ad4..e6e573650250 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 a5a18f74395c..5d87fc86e580 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 33b5ffc8d66d..559fab2a2d67 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 436a62a995ee..53ad0f080179 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 f25f46377399..c2ff29ba6856 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 000000000000..0ec210b4da1d
--- /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 000000000000..75b9c71d9419
--- /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 528f41369364..fb27ff7e1e07 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 379a5e9d52a7..6489c5160ee8 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 539a3f71c16a..3d376faec777 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 c397777d7cbb..617a798d9235 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 53fe2a2b4db3..c7e7f694c6ed 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 d05eaa2c8490..63c68ec7cfa4 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 0741945b79ed..967f6a939340 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 d7db133b493f..61bb0127e19d 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 7ede30b5910f..cf520a7d7f71 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 800aba7ab4a7..03bf95ab017b 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 8cc0d39a2fea..72a343e3b5e8 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 390383941c19..10a7d7bbcc27 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 14bb7bc8adbe..08fbfedea90f 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 47da5e049247..7428b2d22b52 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 bde1f47f7ed3..8d33b68c76ba 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 f079f36099de..1db8cb4c46ef 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 15ef8f28239b..19f556c5957f 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 44d86b61d660..62599fd7756f 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 254d70fe762a..0914230b42de 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 3a6a0dcdc3e4..221ec428a01e 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 548236333cce..f1d192bbcb4c 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 758564649589..540516ca872c 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 d030f9beae88..1f079ff33d4b 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 64ab91edfb81..0821ed08c122 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(&reg->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(&reg->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 01dec9e5fc2b..520c8ded9443 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 c4c17fe76c0d..a634888271cd 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 4cc7f65d7d76..6a17676f9d72 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 f7440e585b6b..c690435853bd 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 d338b19da544..dfd0a21a0658 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 debb44ceb185..d2b440c842b3 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 c4395956a493..7f6f79155537 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 1d7dbd5c0fba..df3a0ec7fd2c 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 60e60aa64fb4..907a205da5a5 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 c5d84c977e29..e71651429dda 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 783f4b05b153..6db245d84bbb 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 908dfd712e8e..3e048ad65647 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 8dac17511e61..a908d006f527 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 70438a0f62ae..40b298ab87f1 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 0d0fab1a7b5e..b38574702fe9 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 1a3b4fc05ec6..a9e3b16460b8 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 d12faa691af8..a130dcdb7206 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 4e86626dad4b..1b34c3629858 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 5733033a6ead..51805a5e2beb 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 ef0efdf422fe..641b1f071e06 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 ab6f9ef89423..0afa90f0f6ab 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 39b77d24b9c2..3dd762e5b67e 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 59a9deefb242..aa4cca371cbf 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 7d0235069c87..7a9c5e9329f2 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 b139b525bb16..626dcccc37da 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 136b7f8ff10d..39186224c16a 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 1abbb36d0755..104485632501 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 a3fd8d63546c..fea3e61476ae 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 5bbeb43e4531..45a0276be4e5 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 5da337736cd8..1047e3e8db77 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 9689a6e89b7f..6769193c7c7b 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 d665242e8207..d4df305fcc18 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 143d29fe0137..f2de0066089a 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 09cb4fc88f34..05dd21a35d63 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 3a4efbdc7668..2dd0e5272941 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, &notifiers);
+ }
+ mutex_unlock(&list_lock);
+
+ list_for_each_entry(sasc, &notifiers, 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 1b7a88ca195b..ceaddfb85e49 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 000000000000..cbd3a34f4f3f
--- /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 000000000000..184a30dff541
--- /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 a2f7bdd5104f..b557caf5b1a4 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 a794cd6c4441..b4f9d03636e3 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 */