aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-05 11:55:59 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-05 11:55:59 -0700
commit27c053aa8d18d1fa7b83041e36bad20bcdf55514 (patch)
treec59dce17a248dd8f4757eca3823032334c626dcd /drivers/media/platform
parenta09e9a7a4b907f2dfa9bdb2b98a1828ab4b340b2 (diff)
parentf66b2a1c7f2ae3fb0d5b67d07ab4f5055fd3cf16 (diff)
Merge branch 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: "This series contains: - Exynos s5p-mfc driver got support for VP8 encoder - Some SoC drivers gained support for asynchronous registration (needed for DT) - The RC subsystem gained support for RC activity LED; - New drivers added: a video decoder(adv7842), a video encoder (adv7511), a new GSPCA driver (stk1135) and support for Renesas R-Car (vsp1) - the first SDR kernel driver: mirics msi3101. Due to some troubles with the driver, and because the API is still under discussion, it will be merged at staging for 3.12. Need to rework on it - usual new boards additions, fixes, cleanups and driver improvements" * 'v4l_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (242 commits) [media] cx88: Fix regression: CX88_AUDIO_WM8775 can't be 0 [media] exynos4-is: Fix entity unregistration on error path [media] exynos-gsc: Register v4l2 device [media] exynos4-is: Fix fimc-lite bayer formats [media] em28xx: fix assignment of the eeprom data [media] hdpvr: fix iteration over uninitialized lists in hdpvr_probe() [media] usbtv: Throw corrupted frames away [media] usbtv: Fix deinterlacing [media] v4l2: added missing mutex.h include to v4l2-ctrls.h [media] DocBook: upgrade media_api DocBook version to 4.2 [media] ml86v7667: fix compile warning: 'ret' set but not used [media] s5p-g2d: Fix registration failure [media] media: coda: Fix DT driver data pointer for i.MX27 [media] s5p-mfc: Fix input/output format reporting [media] v4l: vsp1: Fix mutex double lock at streamon time [media] v4l: vsp1: Add support for RT clock [media] v4l: vsp1: Initialize media device bus_info field [media] davinci: vpif_capture: fix error return code in vpif_probe() [media] davinci: vpif_display: fix error return code in vpif_probe() [media] MAINTAINERS: add entries for adv7511 and adv7842 ...
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig12
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/blackfin/bfin_capture.c9
-rw-r--r--drivers/media/platform/coda.c1482
-rw-r--r--drivers/media/platform/coda.h107
-rw-r--r--drivers/media/platform/davinci/vpbe_display.c23
-rw-r--r--drivers/media/platform/davinci/vpbe_osd.c45
-rw-r--r--drivers/media/platform/davinci/vpbe_venc.c97
-rw-r--r--drivers/media/platform/davinci/vpif_capture.c162
-rw-r--r--drivers/media/platform/davinci/vpif_capture.h2
-rw-r--r--drivers/media/platform/davinci/vpif_display.c221
-rw-r--r--drivers/media/platform/davinci/vpif_display.h3
-rw-r--r--drivers/media/platform/davinci/vpss.c62
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.c22
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-core.h1
-rw-r--r--drivers/media/platform/exynos-gsc/gsc-m2m.c1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-core.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-i2c.c33
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-param.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is-regs.c4
-rw-r--r--drivers/media/platform/exynos4-is/fimc-is.c1
-rw-r--r--drivers/media/platform/exynos4-is/fimc-isp.c2
-rw-r--r--drivers/media/platform/exynos4-is/fimc-lite.c17
-rw-r--r--drivers/media/platform/exynos4-is/media-dev.c17
-rw-r--r--drivers/media/platform/marvell-ccic/cafe-driver.c4
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.c325
-rw-r--r--drivers/media/platform/marvell-ccic/mcam-core.h50
-rw-r--r--drivers/media/platform/marvell-ccic/mmp-driver.c278
-rw-r--r--drivers/media/platform/s3c-camif/camif-regs.c8
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v6.h4
-rw-r--r--drivers/media/platform/s5p-mfc/regs-mfc-v7.h61
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc.c32
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c3
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_common.h23
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c12
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_dec.c11
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c108
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.c2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c149
-rw-r--r--drivers/media/platform/s5p-tv/hdmi_drv.c3
-rw-r--r--drivers/media/platform/soc_camera/Kconfig8
-rw-r--r--drivers/media/platform/soc_camera/Makefile1
-rw-r--r--drivers/media/platform/soc_camera/mx3_camera.c67
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c1486
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c9
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c40
-rw-r--r--drivers/media/platform/vsp1/Makefile5
-rw-r--r--drivers/media/platform/vsp1/vsp1.h74
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c527
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c181
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h68
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.c238
-rw-r--r--drivers/media/platform/vsp1/vsp1_lif.h37
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h581
-rw-r--r--drivers/media/platform/vsp1/vsp1_rpf.c209
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c124
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.h53
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.c346
-rw-r--r--drivers/media/platform/vsp1/vsp1_uds.h40
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c1069
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h144
-rw-r--r--drivers/media/platform/vsp1/vsp1_wpf.c233
63 files changed, 8206 insertions, 740 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 08de865cc399..8068d7b64155 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -203,13 +203,23 @@ config VIDEO_SAMSUNG_EXYNOS_GSC
config VIDEO_SH_VEU
tristate "SuperH VEU mem2mem video processing driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS
+ depends on VIDEO_DEV && VIDEO_V4L2 && GENERIC_HARDIRQS && HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Support for the Video Engine Unit (VEU) on SuperH and
SH-Mobile SoCs.
+config VIDEO_RENESAS_VSP1
+ tristate "Renesas VSP1 Video Processing Engine"
+ depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ ---help---
+ This is a V4L2 driver for the Renesas VSP1 video processing engine.
+
+ To compile this driver as a module, choose M here: the module
+ will be called vsp1.
+
endif # V4L_MEM2MEM_DRIVERS
menuconfig V4L_TEST_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index eee28dd78d7d..4e4da482c522 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -46,6 +46,8 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
+
obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
diff --git a/drivers/media/platform/blackfin/bfin_capture.c b/drivers/media/platform/blackfin/bfin_capture.c
index 7f838c681cea..4c1105977090 100644
--- a/drivers/media/platform/blackfin/bfin_capture.c
+++ b/drivers/media/platform/blackfin/bfin_capture.c
@@ -388,13 +388,8 @@ static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
params.hdelay = bt->hsync + bt->hbackporch;
params.vdelay = bt->vsync + bt->vbackporch;
- params.line = bt->hfrontporch + bt->hsync
- + bt->hbackporch + bt->width;
- params.frame = bt->vfrontporch + bt->vsync
- + bt->vbackporch + bt->height;
- if (bt->interlaced)
- params.frame += bt->il_vfrontporch + bt->il_vsync
- + bt->il_vbackporch;
+ params.line = V4L2_DV_BT_FRAME_WIDTH(bt);
+ params.frame = V4L2_DV_BT_FRAME_HEIGHT(bt);
} else if (bcap_dev->cfg->inputs[bcap_dev->cur_input].capabilities
& V4L2_IN_CAP_STD) {
params.hdelay = 0;
diff --git a/drivers/media/platform/coda.c b/drivers/media/platform/coda.c
index bd9405df1bd6..449d2fec9e87 100644
--- a/drivers/media/platform/coda.c
+++ b/drivers/media/platform/coda.c
@@ -18,6 +18,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/kfifo.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -28,6 +29,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
@@ -41,13 +43,16 @@
#define CODA_FMO_BUF_SIZE 32
#define CODADX6_WORK_BUF_SIZE (288 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
-#define CODA7_WORK_BUF_SIZE (512 * 1024 + CODA_FMO_BUF_SIZE * 8 * 1024)
+#define CODA7_WORK_BUF_SIZE (128 * 1024)
+#define CODA7_TEMP_BUF_SIZE (304 * 1024)
#define CODA_PARA_BUF_SIZE (10 * 1024)
#define CODA_ISRAM_SIZE (2048 * 2)
#define CODADX6_IRAM_SIZE 0xb000
-#define CODA7_IRAM_SIZE 0x14000 /* 81920 bytes */
+#define CODA7_IRAM_SIZE 0x14000
-#define CODA_MAX_FRAMEBUFFERS 2
+#define CODA7_PS_BUF_SIZE 0x28000
+
+#define CODA_MAX_FRAMEBUFFERS 8
#define MAX_W 8192
#define MAX_H 8192
@@ -129,6 +134,7 @@ struct coda_dev {
struct clk *clk_ahb;
struct coda_aux_buf codebuf;
+ struct coda_aux_buf tempbuf;
struct coda_aux_buf workbuf;
struct gen_pool *iram_pool;
long unsigned int iram_vaddr;
@@ -153,6 +159,7 @@ struct coda_params {
u8 mpeg4_inter_qp;
u8 gop_size;
int codec_mode;
+ int codec_mode_aux;
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
u32 framerate;
u16 bitrate;
@@ -160,13 +167,30 @@ struct coda_params {
u32 slice_max_mb;
};
+struct coda_iram_info {
+ u32 axi_sram_use;
+ phys_addr_t buf_bit_use;
+ phys_addr_t buf_ip_ac_dc_use;
+ phys_addr_t buf_dbk_y_use;
+ phys_addr_t buf_dbk_c_use;
+ phys_addr_t buf_ovl_use;
+ phys_addr_t buf_btp_use;
+ phys_addr_t search_ram_paddr;
+ int search_ram_size;
+};
+
struct coda_ctx {
struct coda_dev *dev;
+ struct mutex buffer_mutex;
struct list_head list;
+ struct work_struct skip_run;
int aborting;
+ int initialized;
int streamon_out;
int streamon_cap;
u32 isequence;
+ u32 qsequence;
+ u32 osequence;
struct coda_q_data q_data[2];
enum coda_inst_type inst_type;
struct coda_codec *codec;
@@ -176,12 +200,25 @@ struct coda_ctx {
struct v4l2_ctrl_handler ctrls;
struct v4l2_fh fh;
int gopcounter;
+ int runcounter;
char vpu_header[3][64];
int vpu_header_size[3];
+ struct kfifo bitstream_fifo;
+ struct mutex bitstream_mutex;
+ struct coda_aux_buf bitstream;
+ bool prescan_failed;
struct coda_aux_buf parabuf;
+ struct coda_aux_buf psbuf;
+ struct coda_aux_buf slicebuf;
struct coda_aux_buf internal_frames[CODA_MAX_FRAMEBUFFERS];
+ struct coda_aux_buf workbuf;
int num_internal_frames;
int idx;
+ int reg_idx;
+ struct coda_iram_info iram_info;
+ u32 bit_stream_param;
+ u32 frm_dis_flg;
+ int display_idx;
};
static const u8 coda_filler_nal[14] = { 0x00, 0x00, 0x00, 0x01, 0x0c, 0xff,
@@ -228,10 +265,22 @@ static int coda_wait_timeout(struct coda_dev *dev)
static void coda_command_async(struct coda_ctx *ctx, int cmd)
{
struct coda_dev *dev = ctx->dev;
+
+ if (dev->devtype->product == CODA_7541) {
+ /* Restore context related registers to CODA */
+ coda_write(dev, ctx->bit_stream_param,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ coda_write(dev, ctx->workbuf.paddr, CODA_REG_BIT_WORK_BUF_ADDR);
+ }
+
coda_write(dev, CODA_REG_BIT_BUSY_FLAG, CODA_REG_BIT_BUSY);
coda_write(dev, ctx->idx, CODA_REG_BIT_RUN_INDEX);
coda_write(dev, ctx->params.codec_mode, CODA_REG_BIT_RUN_COD_STD);
+ coda_write(dev, ctx->params.codec_mode_aux, CODA7_REG_BIT_RUN_AUX_STD);
+
coda_write(dev, cmd, CODA_REG_BIT_RUN_COMMAND);
}
@@ -297,6 +346,8 @@ static struct coda_codec codadx6_codecs[] = {
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),
+ CODA_CODEC(CODA7_MODE_DECODE_H264, V4L2_PIX_FMT_H264, V4L2_PIX_FMT_YUV420, 1920, 1080),
+ CODA_CODEC(CODA7_MODE_DECODE_MP4, V4L2_PIX_FMT_MPEG4, V4L2_PIX_FMT_YUV420, 1920, 1080),
};
static bool coda_format_is_yuv(u32 fourcc)
@@ -365,7 +416,7 @@ static int vidioc_querycap(struct file *file, void *priv,
}
static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
- enum v4l2_buf_type type)
+ enum v4l2_buf_type type, int src_fourcc)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
struct coda_codec *codecs = ctx->dev->devtype->codecs;
@@ -377,7 +428,8 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
for (i = 0; i < num_formats; i++) {
/* Both uncompressed formats are always supported */
- if (coda_format_is_yuv(formats[i].fourcc)) {
+ if (coda_format_is_yuv(formats[i].fourcc) &&
+ !coda_format_is_yuv(src_fourcc)) {
if (num == f->index)
break;
++num;
@@ -385,8 +437,10 @@ static int enum_fmt(void *priv, struct v4l2_fmtdesc *f,
}
/* Compressed formats may be supported, check the codec list */
for (k = 0; k < num_codecs; k++) {
+ /* if src_fourcc is set, only consider matching codecs */
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
- formats[i].fourcc == codecs[k].dst_fourcc)
+ formats[i].fourcc == codecs[k].dst_fourcc &&
+ (!src_fourcc || src_fourcc == codecs[k].src_fourcc))
break;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
formats[i].fourcc == codecs[k].src_fourcc)
@@ -413,13 +467,26 @@ 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, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ struct coda_ctx *ctx = fh_to_ctx(priv);
+ struct vb2_queue *src_vq;
+ struct coda_q_data *q_data_src;
+
+ /* If the source format is already fixed, only list matching formats */
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (vb2_is_streaming(src_vq)) {
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ q_data_src->fourcc);
+ }
+
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0);
}
static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ return enum_fmt(priv, f, V4L2_BUF_TYPE_VIDEO_OUTPUT, 0);
}
static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
@@ -492,15 +559,45 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
- struct coda_codec *codec = NULL;
+ struct coda_codec *codec;
+ struct vb2_queue *src_vq;
+ int ret;
- /* Determine codec by the encoded format */
- codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
- f->fmt.pix.pixelformat);
+ /*
+ * If the source format is already fixed, try to find a codec that
+ * converts to the given destination format
+ */
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (vb2_is_streaming(src_vq)) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ codec = coda_find_codec(ctx->dev, q_data_src->fourcc,
+ f->fmt.pix.pixelformat);
+ if (!codec)
+ return -EINVAL;
+ } else {
+ /* Otherwise determine codec by encoded format, if possible */
+ codec = coda_find_codec(ctx->dev, V4L2_PIX_FMT_YUV420,
+ f->fmt.pix.pixelformat);
+ }
f->fmt.pix.colorspace = ctx->colorspace;
- return vidioc_try_fmt(codec, f);
+ ret = vidioc_try_fmt(codec, f);
+ if (ret < 0)
+ return ret;
+
+ /* The h.264 decoder only returns complete 16x16 macroblocks */
+ if (codec && codec->src_fourcc == V4L2_PIX_FMT_H264) {
+ f->fmt.pix.width = round_up(f->fmt.pix.width, 16);
+ f->fmt.pix.height = round_up(f->fmt.pix.height, 16);
+ f->fmt.pix.bytesperline = f->fmt.pix.width;
+ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline *
+ f->fmt.pix.height * 3 / 2;
+ }
+
+ return 0;
}
static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
@@ -610,11 +707,35 @@ static int vidioc_expbuf(struct file *file, void *priv,
return v4l2_m2m_expbuf(file, ctx->m2m_ctx, eb);
}
+static bool coda_buf_is_end_of_stream(struct coda_ctx *ctx,
+ struct v4l2_buffer *buf)
+{
+ struct vb2_queue *src_vq;
+
+ src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+
+ return ((ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) &&
+ (buf->sequence == (ctx->qsequence - 1)));
+}
+
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
+
+ ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+
+ /* If this is the last capture buffer, emit an end-of-stream event */
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ coda_buf_is_end_of_stream(ctx, buf)) {
+ const struct v4l2_event eos_event = {
+ .type = V4L2_EVENT_EOS
+ };
- return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+ v4l2_event_queue_fh(&ctx->fh, &eos_event);
+ }
+
+ return ret;
}
static int vidioc_create_bufs(struct file *file, void *priv,
@@ -637,8 +758,53 @@ static int vidioc_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct coda_ctx *ctx = fh_to_ctx(priv);
+ int ret;
- return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+ /*
+ * This indirectly calls __vb2_queue_cancel, which dequeues all buffers.
+ * We therefore have to lock it against running hardware in this context,
+ * which still needs the buffers.
+ */
+ mutex_lock(&ctx->buffer_mutex);
+ ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+ mutex_unlock(&ctx->buffer_mutex);
+
+ return ret;
+}
+
+static int vidioc_decoder_cmd(struct file *file, void *fh,
+ struct v4l2_decoder_cmd *dc)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+
+ if (dc->cmd != V4L2_DEC_CMD_STOP)
+ return -EINVAL;
+
+ if ((dc->flags & V4L2_DEC_CMD_STOP_TO_BLACK) ||
+ (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY))
+ return -EINVAL;
+
+ if (dc->stop.pts != 0)
+ return -EINVAL;
+
+ if (ctx->inst_type != CODA_INST_DECODER)
+ return -EINVAL;
+
+ /* Set the strem-end flag on this context */
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+ return 0;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ default:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ }
}
static const struct v4l2_ioctl_ops coda_ioctl_ops = {
@@ -664,14 +830,206 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = {
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
+
+ .vidioc_decoder_cmd = vidioc_decoder_cmd,
+
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
+static int coda_start_decoding(struct coda_ctx *ctx);
+
+static void coda_skip_run(struct work_struct *work)
+{
+ struct coda_ctx *ctx = container_of(work, struct coda_ctx, skip_run);
+
+ v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
+}
+
+static inline int coda_get_bitstream_payload(struct coda_ctx *ctx)
+{
+ return kfifo_len(&ctx->bitstream_fifo);
+}
+
+static void coda_kfifo_sync_from_device(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr;
+
+ rd_ptr = coda_read(dev, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ kfifo->out = (kfifo->in & ~kfifo->mask) |
+ (rd_ptr - ctx->bitstream.paddr);
+ if (kfifo->out > kfifo->in)
+ kfifo->out -= kfifo->mask + 1;
+}
+
+static void coda_kfifo_sync_to_device_full(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 rd_ptr, wr_ptr;
+
+ rd_ptr = ctx->bitstream.paddr + (kfifo->out & kfifo->mask);
+ coda_write(dev, rd_ptr, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static void coda_kfifo_sync_to_device_write(struct coda_ctx *ctx)
+{
+ struct __kfifo *kfifo = &ctx->bitstream_fifo.kfifo;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr;
+
+ wr_ptr = ctx->bitstream.paddr + (kfifo->in & kfifo->mask);
+ coda_write(dev, wr_ptr, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+}
+
+static int coda_bitstream_queue(struct coda_ctx *ctx, struct vb2_buffer *src_buf)
+{
+ u32 src_size = vb2_get_plane_payload(src_buf, 0);
+ u32 n;
+
+ n = kfifo_in(&ctx->bitstream_fifo, vb2_plane_vaddr(src_buf, 0), src_size);
+ if (n < src_size)
+ return -ENOSPC;
+
+ dma_sync_single_for_device(&ctx->dev->plat_dev->dev, ctx->bitstream.paddr,
+ ctx->bitstream.size, DMA_TO_DEVICE);
+
+ ctx->qsequence++;
+
+ return 0;
+}
+
+static bool coda_bitstream_try_queue(struct coda_ctx *ctx,
+ struct vb2_buffer *src_buf)
+{
+ int ret;
+
+ if (coda_get_bitstream_payload(ctx) +
+ vb2_get_plane_payload(src_buf, 0) + 512 >= ctx->bitstream.size)
+ return false;
+
+ if (vb2_plane_vaddr(src_buf, 0) == NULL) {
+ v4l2_err(&ctx->dev->v4l2_dev, "trying to queue empty buffer\n");
+ return true;
+ }
+
+ ret = coda_bitstream_queue(ctx, src_buf);
+ if (ret < 0) {
+ v4l2_err(&ctx->dev->v4l2_dev, "bitstream buffer overflow\n");
+ return false;
+ }
+ /* Sync read pointer to device */
+ if (ctx == v4l2_m2m_get_curr_priv(ctx->dev->m2m_dev))
+ coda_kfifo_sync_to_device_write(ctx);
+
+ ctx->prescan_failed = false;
+
+ return true;
+}
+
+static void coda_fill_bitstream(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf;
+
+ while (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) > 0) {
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+
+ if (coda_bitstream_try_queue(ctx, src_buf)) {
+ src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ } else {
+ break;
+ }
+ }
+}
+
/*
* Mem-to-mem operations.
*/
-static void coda_device_run(void *m2m_priv)
+static int coda_prepare_decode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_dst;
+ u32 stridey, height;
+ u32 picture_y, picture_cb, picture_cr;
+
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ if (ctx->params.rot_mode & CODA_ROT_90) {
+ stridey = q_data_dst->height;
+ height = q_data_dst->width;
+ } else {
+ stridey = q_data_dst->width;
+ height = q_data_dst->height;
+ }
+
+ /* Try to copy source buffer contents into the bitstream ringbuffer */
+ mutex_lock(&ctx->bitstream_mutex);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+
+ if (coda_get_bitstream_payload(ctx) < 512 &&
+ (!(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "bitstream payload: %d, skipping\n",
+ coda_get_bitstream_payload(ctx));
+ schedule_work(&ctx->skip_run);
+ return -EAGAIN;
+ }
+
+ /* Run coda_start_decoding (again) if not yet initialized */
+ if (!ctx->initialized) {
+ int ret = coda_start_decoding(ctx);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to start decoding\n");
+ schedule_work(&ctx->skip_run);
+ return -EAGAIN;
+ } else {
+ ctx->initialized = 1;
+ }
+ }
+
+ /* Set rotator output */
+ picture_y = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (q_data_dst->fourcc == V4L2_PIX_FMT_YVU420) {
+ /* Switch Cr and Cb for YVU420 format */
+ picture_cr = picture_y + stridey * height;
+ picture_cb = picture_cr + stridey / 2 * height / 2;
+ } else {
+ picture_cb = picture_y + stridey * height;
+ picture_cr = picture_cb + stridey / 2 * height / 2;
+ }
+ coda_write(dev, picture_y, CODA_CMD_DEC_PIC_ROT_ADDR_Y);
+ coda_write(dev, picture_cb, CODA_CMD_DEC_PIC_ROT_ADDR_CB);
+ coda_write(dev, picture_cr, CODA_CMD_DEC_PIC_ROT_ADDR_CR);
+ coda_write(dev, stridey, CODA_CMD_DEC_PIC_ROT_STRIDE);
+ coda_write(dev, CODA_ROT_MIR_ENABLE | ctx->params.rot_mode,
+ CODA_CMD_DEC_PIC_ROT_MODE);
+
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ /* TBD */
+ case CODA_7541:
+ coda_write(dev, CODA_PRE_SCAN_EN, CODA_CMD_DEC_PIC_OPTION);
+ break;
+ }
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_SKIP_NUM);
+
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_BB_START);
+ coda_write(dev, 0, CODA_CMD_DEC_PIC_START_BYTE);
+
+ return 0;
+}
+
+static void coda_prepare_encode(struct coda_ctx *ctx)
{
- struct coda_ctx *ctx = m2m_priv;
struct coda_q_data *q_data_src, *q_data_dst;
struct vb2_buffer *src_buf, *dst_buf;
struct coda_dev *dev = ctx->dev;
@@ -681,17 +1039,15 @@ 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->fourcc;
- src_buf->v4l2_buf.sequence = ctx->isequence;
- dst_buf->v4l2_buf.sequence = ctx->isequence;
- ctx->isequence++;
+ src_buf->v4l2_buf.sequence = ctx->osequence;
+ dst_buf->v4l2_buf.sequence = ctx->osequence;
+ ctx->osequence++;
/*
* Workaround coda firmware BUG that only marks the first
@@ -793,16 +1149,53 @@ static void coda_device_run(void *m2m_priv)
coda_write(dev, pic_stream_buffer_addr, CODA_CMD_ENC_PIC_BB_START);
coda_write(dev, pic_stream_buffer_size / 1024,
CODA_CMD_ENC_PIC_BB_SIZE);
+}
- if (dev->devtype->product == CODA_7541) {
- coda_write(dev, CODA7_USE_BIT_ENABLE | CODA7_USE_HOST_BIT_ENABLE |
- CODA7_USE_ME_ENABLE | CODA7_USE_HOST_ME_ENABLE,
- CODA7_REG_BIT_AXI_SRAM_USE);
+static void coda_device_run(void *m2m_priv)
+{
+ struct coda_ctx *ctx = m2m_priv;
+ struct coda_dev *dev = ctx->dev;
+ int ret;
+
+ mutex_lock(&ctx->buffer_mutex);
+
+ /*
+ * If streamoff dequeued all buffers before we could get the lock,
+ * just bail out immediately.
+ */
+ if ((!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) &&
+ ctx->inst_type != CODA_INST_DECODER) ||
+ !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "%d: device_run without buffers\n", ctx->idx);
+ mutex_unlock(&ctx->buffer_mutex);
+ schedule_work(&ctx->skip_run);
+ return;
}
+ mutex_lock(&dev->coda_mutex);
+
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ ret = coda_prepare_decode(ctx);
+ if (ret < 0) {
+ mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
+ /* job_finish scheduled by prepare_decode */
+ return;
+ }
+ } else {
+ coda_prepare_encode(ctx);
+ }
+
+ if (dev->devtype->product != CODA_DX6)
+ coda_write(dev, ctx->iram_info.axi_sram_use,
+ CODA7_REG_BIT_AXI_SRAM_USE);
+
/* 1 second timeout in case CODA locks up */
schedule_delayed_work(&dev->timeout, HZ);
+ if (ctx->inst_type == CODA_INST_DECODER)
+ coda_kfifo_sync_to_device_full(ctx);
coda_command_async(ctx, CODA_COMMAND_PIC_RUN);
}
@@ -812,15 +1205,32 @@ static int coda_job_ready(void *m2m_priv)
/*
* For both 'P' and 'key' frame cases 1 picture
- * and 1 frame are needed.
+ * and 1 frame are needed. In the decoder case,
+ * the compressed frame can be in the bitstream.
*/
- if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) ||
- !v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ if (!v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) &&
+ ctx->inst_type != CODA_INST_DECODER) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"not ready: not enough video buffers.\n");
return 0;
}
+ if (!v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "not ready: not enough video capture buffers.\n");
+ return 0;
+ }
+
+ if (ctx->prescan_failed ||
+ ((ctx->inst_type == CODA_INST_DECODER) &&
+ (coda_get_bitstream_payload(ctx) < 512) &&
+ !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "%d: not ready: not enough bitstream data.\n",
+ ctx->idx);
+ return 0;
+ }
+
if (ctx->aborting) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"not ready: aborting\n");
@@ -936,7 +1346,29 @@ static int coda_buf_prepare(struct vb2_buffer *vb)
static void coda_buf_queue(struct vb2_buffer *vb)
{
struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
- v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ struct coda_q_data *q_data;
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ /*
+ * In the decoder case, immediately try to copy the buffer into the
+ * bitstream ringbuffer and mark it as ready to be dequeued.
+ */
+ if (q_data->fourcc == V4L2_PIX_FMT_H264 &&
+ vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ /*
+ * For backwards compatiblity, queuing an empty buffer marks
+ * the stream end
+ */
+ if (vb2_get_plane_payload(vb, 0) == 0)
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+ mutex_lock(&ctx->bitstream_mutex);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ coda_fill_bitstream(ctx);
+ mutex_unlock(&ctx->bitstream_mutex);
+ } else {
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+ }
}
static void coda_wait_prepare(struct vb2_queue *q)
@@ -951,21 +1383,6 @@ static void coda_wait_finish(struct vb2_queue *q)
coda_lock(ctx);
}
-static void coda_free_framebuffers(struct coda_ctx *ctx)
-{
- int i;
-
- for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++) {
- if (ctx->internal_frames[i].vaddr) {
- dma_free_coherent(&ctx->dev->plat_dev->dev,
- ctx->internal_frames[i].size,
- ctx->internal_frames[i].vaddr,
- ctx->internal_frames[i].paddr);
- ctx->internal_frames[i].vaddr = NULL;
- }
- }
-}
-
static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
{
struct coda_dev *dev = ctx->dev;
@@ -977,29 +1394,69 @@ static void coda_parabuf_write(struct coda_ctx *ctx, int index, u32 value)
p[index ^ 1] = value;
}
+static int coda_alloc_aux_buf(struct coda_dev *dev,
+ struct coda_aux_buf *buf, size_t size)
+{
+ buf->vaddr = dma_alloc_coherent(&dev->plat_dev->dev, size, &buf->paddr,
+ GFP_KERNEL);
+ if (!buf->vaddr)
+ return -ENOMEM;
+
+ buf->size = size;
+
+ return 0;
+}
+
+static inline int coda_alloc_context_buf(struct coda_ctx *ctx,
+ struct coda_aux_buf *buf, size_t size)
+{
+ return coda_alloc_aux_buf(ctx->dev, buf, size);
+}
+
+static void coda_free_aux_buf(struct coda_dev *dev,
+ struct coda_aux_buf *buf)
+{
+ if (buf->vaddr) {
+ dma_free_coherent(&dev->plat_dev->dev, buf->size,
+ buf->vaddr, buf->paddr);
+ buf->vaddr = NULL;
+ buf->size = 0;
+ }
+}
+
+static void coda_free_framebuffers(struct coda_ctx *ctx)
+{
+ int i;
+
+ for (i = 0; i < CODA_MAX_FRAMEBUFFERS; i++)
+ coda_free_aux_buf(ctx->dev, &ctx->internal_frames[i]);
+}
+
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;
dma_addr_t paddr;
int ysize;
+ int ret;
int i;
+ if (ctx->codec && ctx->codec->src_fourcc == V4L2_PIX_FMT_H264)
+ height = round_up(height, 16);
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)
+ size_t size;
+
+ size = q_data->sizeimage;
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
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);
- if (!ctx->internal_frames[i].vaddr) {
+ ret = coda_alloc_context_buf(ctx, &ctx->internal_frames[i], size);
+ if (ret < 0) {
coda_free_framebuffers(ctx);
- return -ENOMEM;
+ return ret;
}
}
@@ -1010,10 +1467,20 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, struct coda_q_data *q_d
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 && fourcc == V4L2_PIX_FMT_H264)
- coda_parabuf_write(ctx, 96 + i, ctx->internal_frames[i].paddr + ysize + ysize/4 + ysize/4);
+ /* mvcol buffer for h.264 */
+ if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
+ dev->devtype->product != CODA_DX6)
+ coda_parabuf_write(ctx, 96 + i,
+ ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
}
+ /* mvcol buffer for mpeg4 */
+ if ((dev->devtype->product != CODA_DX6) &&
+ (ctx->codec->src_fourcc == V4L2_PIX_FMT_MPEG4))
+ coda_parabuf_write(ctx, 97, ctx->internal_frames[i].paddr +
+ ysize + ysize/4 + ysize/4);
+
return 0;
}
@@ -1035,6 +1502,371 @@ static int coda_h264_padding(int size, char *p)
return nal_size;
}
+static void coda_setup_iram(struct coda_ctx *ctx)
+{
+ struct coda_iram_info *iram_info = &ctx->iram_info;
+ struct coda_dev *dev = ctx->dev;
+ int ipacdc_size;
+ int bitram_size;
+ int dbk_size;
+ int ovl_size;
+ int mb_width;
+ int me_size;
+ int size;
+
+ memset(iram_info, 0, sizeof(*iram_info));
+ size = dev->iram_size;
+
+ if (dev->devtype->product == CODA_DX6)
+ return;
+
+ if (ctx->inst_type == CODA_INST_ENCODER) {
+ struct coda_q_data *q_data_src;
+
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ mb_width = DIV_ROUND_UP(q_data_src->width, 16);
+
+ /* Prioritize in case IRAM is too small for everything */
+ me_size = round_up(round_up(q_data_src->width, 16) * 36 + 2048,
+ 1024);
+ iram_info->search_ram_size = me_size;
+ if (size >= iram_info->search_ram_size) {
+ if (dev->devtype->product == CODA_7541)
+ iram_info->axi_sram_use |= CODA7_USE_HOST_ME_ENABLE;
+ iram_info->search_ram_paddr = dev->iram_paddr;
+ size -= iram_info->search_ram_size;
+ } else {
+ pr_err("IRAM is smaller than the search ram size\n");
+ goto out;
+ }
+
+ /* Only H.264BP and H.263P3 are considered */
+ dbk_size = round_up(128 * mb_width, 1024);
+ if (size >= dbk_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE;
+ iram_info->buf_dbk_y_use = dev->iram_paddr +
+ iram_info->search_ram_size;
+ iram_info->buf_dbk_c_use = iram_info->buf_dbk_y_use +
+ dbk_size / 2;
+ size -= dbk_size;
+ } else {
+ goto out;
+ }
+
+ bitram_size = round_up(128 * mb_width, 1024);
+ if (size >= bitram_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE;
+ iram_info->buf_bit_use = iram_info->buf_dbk_c_use +
+ dbk_size / 2;
+ size -= bitram_size;
+ } else {
+ goto out;
+ }
+
+ ipacdc_size = round_up(128 * mb_width, 1024);
+ if (size >= ipacdc_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE;
+ iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use +
+ bitram_size;
+ size -= ipacdc_size;
+ }
+
+ /* OVL and BTP disabled for encoder */
+ } else if (ctx->inst_type == CODA_INST_DECODER) {
+ struct coda_q_data *q_data_dst;
+ int mb_height;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ mb_width = DIV_ROUND_UP(q_data_dst->width, 16);
+ mb_height = DIV_ROUND_UP(q_data_dst->height, 16);
+
+ dbk_size = round_up(256 * mb_width, 1024);
+ if (size >= dbk_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_DBK_ENABLE;
+ iram_info->buf_dbk_y_use = dev->iram_paddr;
+ iram_info->buf_dbk_c_use = dev->iram_paddr +
+ dbk_size / 2;
+ size -= dbk_size;
+ } else {
+ goto out;
+ }
+
+ bitram_size = round_up(128 * mb_width, 1024);
+ if (size >= bitram_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_BIT_ENABLE;
+ iram_info->buf_bit_use = iram_info->buf_dbk_c_use +
+ dbk_size / 2;
+ size -= bitram_size;
+ } else {
+ goto out;
+ }
+
+ ipacdc_size = round_up(128 * mb_width, 1024);
+ if (size >= ipacdc_size) {
+ iram_info->axi_sram_use |= CODA7_USE_HOST_IP_ENABLE;
+ iram_info->buf_ip_ac_dc_use = iram_info->buf_bit_use +
+ bitram_size;
+ size -= ipacdc_size;
+ } else {
+ goto out;
+ }
+
+ ovl_size = round_up(80 * mb_width, 1024);
+ }
+
+out:
+ switch (dev->devtype->product) {
+ case CODA_DX6:
+ break;
+ case CODA_7541:
+ /* i.MX53 uses secondary AXI for IRAM access */
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_BIT_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_BIT_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_IP_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_DBK_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_DBK_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_OVL_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_OVL_ENABLE;
+ if (iram_info->axi_sram_use & CODA7_USE_HOST_ME_ENABLE)
+ iram_info->axi_sram_use |= CODA7_USE_ME_ENABLE;
+ }
+
+ if (!(iram_info->axi_sram_use & CODA7_USE_HOST_IP_ENABLE))
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "IRAM smaller than needed\n");
+
+ if (dev->devtype->product == CODA_7541) {
+ /* TODO - Enabling these causes picture errors on CODA7541 */
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ /* fw 1.4.50 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_IP_ENABLE);
+ } else {
+ /* fw 13.4.29 */
+ iram_info->axi_sram_use &= ~(CODA7_USE_HOST_IP_ENABLE |
+ CODA7_USE_HOST_DBK_ENABLE |
+ CODA7_USE_IP_ENABLE |
+ CODA7_USE_DBK_ENABLE);
+ }
+ }
+}
+
+static void coda_free_context_buffers(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+
+ coda_free_aux_buf(dev, &ctx->slicebuf);
+ coda_free_aux_buf(dev, &ctx->psbuf);
+ if (dev->devtype->product != CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+}
+
+static int coda_alloc_context_buffers(struct coda_ctx *ctx,
+ struct coda_q_data *q_data)
+{
+ struct coda_dev *dev = ctx->dev;
+ size_t size;
+ int ret;
+
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ size = CODA7_WORK_BUF_SIZE;
+ break;
+ default:
+ return 0;
+ }
+
+ if (ctx->psbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "psmembuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->slicebuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "slicebuf still allocated\n");
+ return -EBUSY;
+ }
+ if (ctx->workbuf.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "context buffer still allocated\n");
+ ret = -EBUSY;
+ return -ENOMEM;
+ }
+
+ if (q_data->fourcc == V4L2_PIX_FMT_H264) {
+ /* worst case slice size */
+ size = (DIV_ROUND_UP(q_data->width, 16) *
+ DIV_ROUND_UP(q_data->height, 16)) * 3200 / 8 + 512;
+ ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte slice buffer",
+ ctx->slicebuf.size);
+ return ret;
+ }
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ ret = coda_alloc_context_buf(ctx, &ctx->psbuf, CODA7_PS_BUF_SIZE);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate psmem buffer");
+ goto err;
+ }
+ }
+
+ ret = coda_alloc_context_buf(ctx, &ctx->workbuf, size);
+ if (ret < 0) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate %d byte context buffer",
+ ctx->workbuf.size);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ coda_free_context_buffers(ctx);
+ return ret;
+}
+
+static int coda_start_decoding(struct coda_ctx *ctx)
+{
+ struct coda_q_data *q_data_src, *q_data_dst;
+ u32 bitstream_buf, bitstream_size;
+ struct coda_dev *dev = ctx->dev;
+ int width, height;
+ u32 src_fourcc;
+ u32 val;
+ int ret;
+
+ /* Start decoding */
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ bitstream_buf = ctx->bitstream.paddr;
+ bitstream_size = ctx->bitstream.size;
+ src_fourcc = q_data_src->fourcc;
+
+ coda_write(dev, ctx->parabuf.paddr, CODA_REG_BIT_PARA_BUF_ADDR);
+
+ /* Update coda bitstream read and write pointers from kfifo */
+ coda_kfifo_sync_to_device_full(ctx);
+
+ ctx->display_idx = -1;
+ ctx->frm_dis_flg = 0;
+ coda_write(dev, 0, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ coda_write(dev, CODA_BIT_DEC_SEQ_INIT_ESCAPE,
+ CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ coda_write(dev, bitstream_buf, CODA_CMD_DEC_SEQ_BB_START);
+ coda_write(dev, bitstream_size / 1024, CODA_CMD_DEC_SEQ_BB_SIZE);
+ val = 0;
+ if (dev->devtype->product == CODA_7541)
+ val |= CODA_REORDER_ENABLE;
+ coda_write(dev, val, CODA_CMD_DEC_SEQ_OPTION);
+
+ ctx->params.codec_mode = ctx->codec->mode;
+ ctx->params.codec_mode_aux = 0;
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, ctx->psbuf.paddr,
+ CODA_CMD_DEC_SEQ_PS_BB_START);
+ coda_write(dev, (CODA7_PS_BUF_SIZE / 1024),
+ CODA_CMD_DEC_SEQ_PS_BB_SIZE);
+ }
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SEQ_INIT)) {
+ v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_SEQ_INIT timeout\n");
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ return -ETIMEDOUT;
+ }
+
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
+
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+
+ if (coda_read(dev, CODA_RET_DEC_SEQ_SUCCESS) == 0) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_INIT failed, error code = %d\n",
+ coda_read(dev, CODA_RET_DEC_SEQ_ERR_REASON));
+ return -EAGAIN;
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_SEQ_SRC_SIZE);
+ if (dev->devtype->product == CODA_DX6) {
+ width = (val >> CODADX6_PICWIDTH_OFFSET) & CODADX6_PICWIDTH_MASK;
+ height = val & CODADX6_PICHEIGHT_MASK;
+ } else {
+ width = (val >> CODA7_PICWIDTH_OFFSET) & CODA7_PICWIDTH_MASK;
+ height = val & CODA7_PICHEIGHT_MASK;
+ }
+
+ if (width > q_data_dst->width || height > q_data_dst->height) {
+ v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n",
+ width, height, q_data_dst->width, q_data_dst->height);
+ return -EINVAL;
+ }
+
+ width = round_up(width, 16);
+ height = round_up(height, 16);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "%s instance %d now: %dx%d\n",
+ __func__, ctx->idx, width, height);
+
+ ctx->num_internal_frames = coda_read(dev, CODA_RET_DEC_SEQ_FRAME_NEED) + 1;
+ if (ctx->num_internal_frames > CODA_MAX_FRAMEBUFFERS) {
+ v4l2_err(&dev->v4l2_dev,
+ "not enough framebuffers to decode (%d < %d)\n",
+ CODA_MAX_FRAMEBUFFERS, ctx->num_internal_frames);
+ return -EINVAL;
+ }
+
+ ret = coda_alloc_framebuffers(ctx, q_data_dst, src_fourcc);
+ if (ret < 0)
+ return ret;
+
+ /* Tell the decoder how many frame buffers we allocated. */
+ coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM);
+ coda_write(dev, width, CODA_CMD_SET_FRAME_BUF_STRIDE);
+
+ if (dev->devtype->product != CODA_DX6) {
+ /* Set secondary AXI IRAM */
+ coda_setup_iram(ctx);
+
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
+ }
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ coda_write(dev, ctx->slicebuf.paddr,
+ CODA_CMD_SET_FRAME_SLICE_BB_START);
+ coda_write(dev, ctx->slicebuf.size / 1024,
+ CODA_CMD_SET_FRAME_SLICE_BB_SIZE);
+ }
+
+ if (dev->devtype->product == CODA_7541) {
+ int max_mb_x = 1920 / 16;
+ int max_mb_y = 1088 / 16;
+ int max_mb_num = max_mb_x * max_mb_y;
+ coda_write(dev, max_mb_num << 16 | max_mb_x << 8 | max_mb_y,
+ CODA7_CMD_SET_FRAME_MAX_DEC_SIZE);
+ }
+
+ if (coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "CODA_COMMAND_SET_FRAME_BUF timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
int header_code, u8 *header, int *size)
{
@@ -1050,7 +1882,7 @@ static int coda_encode_header(struct coda_ctx *ctx, struct vb2_buffer *buf,
v4l2_err(&dev->v4l2_dev, "CODA_COMMAND_ENCODE_HEADER timeout\n");
return ret;
}
- *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx)) -
+ *size = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx)) -
coda_read(dev, CODA_CMD_ENC_HEADER_BB_START);
memcpy(header, vb2_plane_vaddr(buf, 0), *size);
@@ -1069,26 +1901,36 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
u32 value;
int ret = 0;
- if (count < 1)
- return -EINVAL;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (q_data_src->fourcc == V4L2_PIX_FMT_H264) {
+ if (coda_get_bitstream_payload(ctx) < 512)
+ return -EINVAL;
+ } else {
+ if (count < 1)
+ return -EINVAL;
+ }
- if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
ctx->streamon_out = 1;
- else
- 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;
+ } else {
+ if (count < 1)
+ return -EINVAL;
+
+ ctx->streamon_cap = 1;
}
/* Don't start the coda unless both queues are on */
if (!(ctx->streamon_out & ctx->streamon_cap))
return 0;
+ /* Allow device_run with no buffers queued and after streamoff */
+ v4l2_m2m_set_src_buffered(ctx->m2m_ctx, true);
+
ctx->gopcounter = ctx->params.gop_size - 1;
buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
bitstream_buf = vb2_dma_contig_plane_dma_addr(buf, 0);
@@ -1103,6 +1945,25 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
return -EINVAL;
}
+ /* Allocate per-instance buffers */
+ ret = coda_alloc_context_buffers(ctx, q_data_src);
+ if (ret < 0)
+ return ret;
+
+ if (ctx->inst_type == CODA_INST_DECODER) {
+ mutex_lock(&dev->coda_mutex);
+ ret = coda_start_decoding(ctx);
+ mutex_unlock(&dev->coda_mutex);
+ if (ret == -EAGAIN) {
+ return 0;
+ } else if (ret < 0) {
+ return ret;
+ } else {
+ ctx->initialized = 1;
+ return 0;
+ }
+ }
+
if (!coda_is_initialized(dev)) {
v4l2_err(v4l2_dev, "coda is not initialized.\n");
return -EFAULT;
@@ -1111,8 +1972,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
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));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_RD_PTR(ctx->reg_idx));
+ coda_write(dev, bitstream_buf, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
switch (dev->devtype->product) {
case CODA_DX6:
coda_write(dev, CODADX6_STREAM_BUF_DYNALLOC_EN |
@@ -1207,6 +2068,8 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
}
coda_write(dev, value, CODA_CMD_ENC_SEQ_OPTION);
+ coda_setup_iram(ctx);
+
if (dst_fourcc == V4L2_PIX_FMT_H264) {
value = (FMO_SLICE_SAVE_BUF_SIZE << 7);
value |= (0 & CODA_FMOPARAM_TYPE_MASK) << CODA_FMOPARAM_TYPE_OFFSET;
@@ -1214,8 +2077,10 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
if (dev->devtype->product == CODA_DX6) {
coda_write(dev, value, CODADX6_CMD_ENC_SEQ_FMO);
} else {
- coda_write(dev, dev->iram_paddr, CODA7_CMD_ENC_SEQ_SEARCH_BASE);
- coda_write(dev, 48 * 1024, CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
+ coda_write(dev, ctx->iram_info.search_ram_paddr,
+ CODA7_CMD_ENC_SEQ_SEARCH_BASE);
+ coda_write(dev, ctx->iram_info.search_ram_size,
+ CODA7_CMD_ENC_SEQ_SEARCH_SIZE);
}
}
@@ -1231,6 +2096,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
goto out;
}
+ ctx->num_internal_frames = 2;
ret = coda_alloc_framebuffers(ctx, q_data_src, dst_fourcc);
if (ret < 0) {
v4l2_err(v4l2_dev, "failed to allocate framebuffers\n");
@@ -1239,13 +2105,20 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
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);
+ if (dev->devtype->product == CODA_7541)
+ coda_write(dev, round_up(q_data_src->width, 8),
+ CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
if (dev->devtype->product != CODA_DX6) {
- coda_write(dev, round_up(q_data_src->width, 8), CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE);
- coda_write(dev, dev->iram_paddr + 48 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
- coda_write(dev, dev->iram_paddr + 53 * 1024, CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
- coda_write(dev, dev->iram_paddr + 58 * 1024, CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
- 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);
+ coda_write(dev, ctx->iram_info.buf_bit_use,
+ CODA7_CMD_SET_FRAME_AXI_BIT_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ip_ac_dc_use,
+ CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_y_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR);
+ coda_write(dev, ctx->iram_info.buf_dbk_c_use,
+ CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
+ coda_write(dev, ctx->iram_info.buf_ovl_use,
+ CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
}
ret = coda_command_sync(ctx, CODA_COMMAND_SET_FRAME_BUF);
if (ret < 0) {
@@ -1326,32 +2199,26 @@ static int coda_stop_streaming(struct vb2_queue *q)
struct coda_dev *dev = ctx->dev;
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%s: output\n", __func__);
ctx->streamon_out = 0;
+
+ ctx->bit_stream_param |= CODA_BIT_STREAM_END_FLAG;
+
+ ctx->isequence = 0;
} else {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%s: capture\n", __func__);
ctx->streamon_cap = 0;
- }
-
- /* Don't stop the coda unless both queues are off */
- if (ctx->streamon_out || ctx->streamon_cap)
- return 0;
- 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)) {
- v4l2_err(&dev->v4l2_dev,
- "CODA_COMMAND_SEQ_END failed\n");
- return -ETIMEDOUT;
+ ctx->osequence = 0;
}
- mutex_unlock(&dev->coda_mutex);
- coda_free_framebuffers(ctx);
+ if (!ctx->streamon_out && !ctx->streamon_cap) {
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ ctx->runcounter = 0;
+ }
return 0;
}
@@ -1511,23 +2378,41 @@ static int coda_open(struct file *file)
{
struct coda_dev *dev = video_drvdata(file);
struct coda_ctx *ctx = NULL;
- int ret = 0;
+ int ret;
int idx;
- idx = coda_next_free_instance(dev);
- if (idx >= CODA_MAX_INSTANCES)
- return -EBUSY;
- set_bit(idx, &dev->instance_mask);
-
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
+ idx = coda_next_free_instance(dev);
+ if (idx >= CODA_MAX_INSTANCES) {
+ ret = -EBUSY;
+ goto err_coda_max;
+ }
+ set_bit(idx, &dev->instance_mask);
+
+ INIT_WORK(&ctx->skip_run, coda_skip_run);
v4l2_fh_init(&ctx->fh, video_devdata(file));
file->private_data = &ctx->fh;
v4l2_fh_add(&ctx->fh);
ctx->dev = dev;
ctx->idx = idx;
+ switch (dev->devtype->product) {
+ case CODA_7541:
+ ctx->reg_idx = 0;
+ break;
+ default:
+ ctx->reg_idx = idx;
+ }
+
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ goto err_clk_per;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
set_default_params(ctx);
ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx,
@@ -1537,39 +2422,62 @@ static int coda_open(struct file *file)
v4l2_err(&dev->v4l2_dev, "%s return error (%d)\n",
__func__, ret);
- goto err;
+ goto err_ctx_init;
}
ret = coda_ctrls_setup(ctx);
if (ret) {
v4l2_err(&dev->v4l2_dev, "failed to setup coda controls\n");
- goto err;
+ goto err_ctrls_setup;
}
ctx->fh.ctrl_handler = &ctx->ctrls;
- ctx->parabuf.vaddr = dma_alloc_coherent(&dev->plat_dev->dev,
- CODA_PARA_BUF_SIZE, &ctx->parabuf.paddr, GFP_KERNEL);
- if (!ctx->parabuf.vaddr) {
+ ret = coda_alloc_context_buf(ctx, &ctx->parabuf, CODA_PARA_BUF_SIZE);
+ if (ret < 0) {
v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf");
+ goto err_dma_alloc;
+ }
+
+ ctx->bitstream.size = CODA_MAX_FRAME_SIZE;
+ ctx->bitstream.vaddr = dma_alloc_writecombine(&dev->plat_dev->dev,
+ ctx->bitstream.size, &ctx->bitstream.paddr, GFP_KERNEL);
+ if (!ctx->bitstream.vaddr) {
+ v4l2_err(&dev->v4l2_dev, "failed to allocate bitstream ringbuffer");
ret = -ENOMEM;
- goto err;
+ goto err_dma_writecombine;
}
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+ mutex_init(&ctx->bitstream_mutex);
+ mutex_init(&ctx->buffer_mutex);
coda_lock(ctx);
list_add(&ctx->list, &dev->instances);
coda_unlock(ctx);
- clk_prepare_enable(dev->clk_per);
- clk_prepare_enable(dev->clk_ahb);
-
v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Created instance %d (%p)\n",
ctx->idx, ctx);
return 0;
-err:
+err_dma_writecombine:
+ coda_free_context_buffers(ctx);
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+ coda_free_aux_buf(dev, &ctx->parabuf);
+err_dma_alloc:
+ v4l2_ctrl_handler_free(&ctx->ctrls);
+err_ctrls_setup:
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+err_ctx_init:
+ clk_disable_unprepare(dev->clk_ahb);
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+err_clk_per:
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
+ clear_bit(ctx->idx, &dev->instance_mask);
+err_coda_max:
kfree(ctx);
return ret;
}
@@ -1582,16 +2490,37 @@ static int coda_release(struct file *file)
v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n",
ctx);
+ /* If this instance is running, call .job_abort and wait for it to end */
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+
+ /* In case the instance was not running, we still need to call SEQ_END */
+ 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)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ mutex_unlock(&dev->coda_mutex);
+ return -ETIMEDOUT;
+ }
+ mutex_unlock(&dev->coda_mutex);
+
+ coda_free_framebuffers(ctx);
+
coda_lock(ctx);
list_del(&ctx->list);
coda_unlock(ctx);
- dma_free_coherent(&dev->plat_dev->dev, CODA_PARA_BUF_SIZE,
- ctx->parabuf.vaddr, ctx->parabuf.paddr);
- v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ dma_free_writecombine(&dev->plat_dev->dev, ctx->bitstream.size,
+ ctx->bitstream.vaddr, ctx->bitstream.paddr);
+ coda_free_context_buffers(ctx);
+ if (ctx->dev->devtype->product == CODA_DX6)
+ coda_free_aux_buf(dev, &ctx->workbuf);
+
+ coda_free_aux_buf(dev, &ctx->parabuf);
v4l2_ctrl_handler_free(&ctx->ctrls);
- clk_disable_unprepare(dev->clk_per);
clk_disable_unprepare(dev->clk_ahb);
+ clk_disable_unprepare(dev->clk_per);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
clear_bit(ctx->idx, &dev->instance_mask);
@@ -1628,55 +2557,180 @@ static const struct v4l2_file_operations coda_fops = {
.mmap = coda_mmap,
};
-static irqreturn_t coda_irq_handler(int irq, void *data)
+static void coda_finish_decode(struct coda_ctx *ctx)
{
- struct vb2_buffer *src_buf, *dst_buf;
- struct coda_dev *dev = data;
- u32 wr_ptr, start_ptr;
- struct coda_ctx *ctx;
+ struct coda_dev *dev = ctx->dev;
+ struct coda_q_data *q_data_src;
+ struct coda_q_data *q_data_dst;
+ struct vb2_buffer *dst_buf;
+ int width, height;
+ int decoded_idx;
+ int display_idx;
+ u32 src_fourcc;
+ int success;
+ u32 val;
- cancel_delayed_work(&dev->timeout);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
- /* read status register to attend the IRQ */
- coda_read(dev, CODA_REG_BIT_INT_STATUS);
- coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
- CODA_REG_BIT_INT_CLEAR);
+ /* Update kfifo out pointer from coda bitstream read pointer */
+ coda_kfifo_sync_from_device(ctx);
- 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;
+ /*
+ * in stream-end mode, the read pointer can overshoot the write pointer
+ * by up to 512 bytes
+ */
+ if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG) {
+ if (coda_get_bitstream_payload(ctx) >= 0x100000 - 512)
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
}
- 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;
+ q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ src_fourcc = q_data_src->fourcc;
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_SUCCESS);
+ if (val != 1)
+ pr_err("DEC_PIC_SUCCESS = %d\n", val);
+
+ success = val & 0x1;
+ if (!success)
+ v4l2_err(&dev->v4l2_dev, "decode failed\n");
+
+ if (src_fourcc == V4L2_PIX_FMT_H264) {
+ if (val & (1 << 3))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient PS buffer space (%d bytes)\n",
+ ctx->psbuf.size);
+ if (val & (1 << 2))
+ v4l2_err(&dev->v4l2_dev,
+ "insufficient slice buffer space (%d bytes)\n",
+ ctx->slicebuf.size);
}
- if (coda_isbusy(ctx->dev)) {
- v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
- "coda is still busy!!!!\n");
- return IRQ_NONE;
+ val = coda_read(dev, CODA_RET_DEC_PIC_SIZE);
+ width = (val >> 16) & 0xffff;
+ height = val & 0xffff;
+
+ q_data_dst = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_TYPE);
+ if ((val & 0x7) == 0) {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_KEYFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_PFRAME;
+ } else {
+ dst_buf->v4l2_buf.flags |= V4L2_BUF_FLAG_PFRAME;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_KEYFRAME;
+ }
+
+ val = coda_read(dev, CODA_RET_DEC_PIC_ERR_MB);
+ if (val > 0)
+ v4l2_err(&dev->v4l2_dev,
+ "errors in %d macroblocks\n", val);
+
+ if (dev->devtype->product == CODA_7541) {
+ val = coda_read(dev, CODA_RET_DEC_PIC_OPTION);
+ if (val == 0) {
+ /* not enough bitstream data */
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "prescan failed: %d\n", val);
+ ctx->prescan_failed = true;
+ return;
+ }
+ }
+
+ ctx->frm_dis_flg = coda_read(dev, CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+
+ /*
+ * The previous display frame was copied out by the rotator,
+ * now it can be overwritten again
+ */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ ctx->frm_dis_flg &= ~(1 << ctx->display_idx);
+ coda_write(dev, ctx->frm_dis_flg,
+ CODA_REG_BIT_FRM_DIS_FLG(ctx->reg_idx));
+ }
+
+ /*
+ * The index of the last decoded frame, not necessarily in
+ * display order, and the index of the next display frame.
+ * The latter could have been decoded in a previous run.
+ */
+ decoded_idx = coda_read(dev, CODA_RET_DEC_PIC_CUR_IDX);
+ display_idx = coda_read(dev, CODA_RET_DEC_PIC_FRAME_IDX);
+
+ if (decoded_idx == -1) {
+ /* no frame was decoded, but we might have a display frame */
+ if (display_idx < 0 && ctx->display_idx < 0)
+ ctx->prescan_failed = true;
+ } else if (decoded_idx == -2) {
+ /* no frame was decoded, we still return the remaining buffers */
+ } else if (decoded_idx < 0 || decoded_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "decoded frame index out of range: %d\n", decoded_idx);
}
+ if (display_idx == -1) {
+ /*
+ * no more frames to be decoded, but there could still
+ * be rotator output to dequeue
+ */
+ ctx->prescan_failed = true;
+ } else if (display_idx == -3) {
+ /* possibly prescan failure */
+ } else if (display_idx < 0 || display_idx >= ctx->num_internal_frames) {
+ v4l2_err(&dev->v4l2_dev,
+ "presentation frame index out of range: %d\n",
+ display_idx);
+ }
+
+ /* If a frame was copied out, return it */
+ if (ctx->display_idx >= 0 &&
+ ctx->display_idx < ctx->num_internal_frames) {
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
+ dst_buf->v4l2_buf.sequence = ctx->osequence++;
+
+ vb2_set_plane_payload(dst_buf, 0, width * height * 3 / 2);
+
+ v4l2_m2m_buf_done(dst_buf, success ? VB2_BUF_STATE_DONE :
+ VB2_BUF_STATE_ERROR);
+
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: decoding frame (%d) (%s)\n",
+ dst_buf->v4l2_buf.sequence,
+ (dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
+ "KEYFRAME" : "PFRAME");
+ } else {
+ v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
+ "job finished: no frame decoded\n");
+ }
+
+ /* The rotator will copy the current display frame next time */
+ ctx->display_idx = display_idx;
+}
+
+static void coda_finish_encode(struct coda_ctx *ctx)
+{
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct coda_dev *dev = ctx->dev;
+ u32 wr_ptr, start_ptr;
+
src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
/* Get results from the coda */
coda_read(dev, CODA_RET_ENC_PIC_TYPE);
start_ptr = coda_read(dev, CODA_CMD_ENC_PIC_BB_START);
- wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->idx));
+ wr_ptr = coda_read(dev, CODA_REG_BIT_WR_PTR(ctx->reg_idx));
+
/* Calculate bytesused field */
if (dst_buf->v4l2_buf.sequence == 0) {
- dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr) +
- ctx->vpu_header_size[0] +
- ctx->vpu_header_size[1] +
- ctx->vpu_header_size[2];
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr +
+ ctx->vpu_header_size[0] +
+ ctx->vpu_header_size[1] +
+ ctx->vpu_header_size[2]);
} else {
- dst_buf->v4l2_planes[0].bytesused = (wr_ptr - start_ptr);
+ vb2_set_plane_payload(dst_buf, 0, wr_ptr - start_ptr);
}
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, "frame size = %u\n",
@@ -1708,8 +2762,62 @@ static irqreturn_t coda_irq_handler(int irq, void *data)
dst_buf->v4l2_buf.sequence,
(dst_buf->v4l2_buf.flags & V4L2_BUF_FLAG_KEYFRAME) ?
"KEYFRAME" : "PFRAME");
+}
+
+static irqreturn_t coda_irq_handler(int irq, void *data)
+{
+ struct coda_dev *dev = data;
+ struct coda_ctx *ctx;
+
+ cancel_delayed_work(&dev->timeout);
+
+ /* read status register to attend the IRQ */
+ coda_read(dev, CODA_REG_BIT_INT_STATUS);
+ coda_write(dev, CODA_REG_BIT_INT_CLEAR_SET,
+ CODA_REG_BIT_INT_CLEAR);
+
+ 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");
+ goto out;
+ }
+
+ if (coda_isbusy(ctx->dev)) {
+ v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
+ "coda is still busy!!!!\n");
+ return IRQ_NONE;
+ }
+
+ if (ctx->inst_type == CODA_INST_DECODER)
+ coda_finish_decode(ctx);
+ else
+ coda_finish_encode(ctx);
+
+out:
+ if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) {
+ 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)) {
+ v4l2_err(&dev->v4l2_dev,
+ "CODA_COMMAND_SEQ_END failed\n");
+ }
+
+ kfifo_init(&ctx->bitstream_fifo,
+ ctx->bitstream.vaddr, ctx->bitstream.size);
+
+ coda_free_framebuffers(ctx);
+ coda_free_context_buffers(ctx);
+ }
mutex_unlock(&dev->coda_mutex);
+ mutex_unlock(&ctx->buffer_mutex);
v4l2_m2m_job_finish(ctx->dev->m2m_dev, ctx->m2m_ctx);
@@ -1726,6 +2834,8 @@ static void coda_timeout(struct work_struct *work)
mutex_lock(&dev->dev_mutex);
list_for_each_entry(ctx, &dev->instances, list) {
+ if (mutex_is_locked(&ctx->buffer_mutex))
+ mutex_unlock(&ctx->buffer_mutex);
v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
v4l2_m2m_streamoff(NULL, ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
}
@@ -1738,7 +2848,7 @@ static void coda_timeout(struct work_struct *work)
static u32 coda_supported_firmwares[] = {
CODA_FIRMWARE_VERNUM(CODA_DX6, 2, 2, 5),
- CODA_FIRMWARE_VERNUM(CODA_7541, 13, 4, 29),
+ CODA_FIRMWARE_VERNUM(CODA_7541, 1, 4, 50),
};
static bool coda_firmware_supported(u32 vernum)
@@ -1771,10 +2881,15 @@ static int coda_hw_init(struct coda_dev *dev)
u16 product, major, minor, release;
u32 data;
u16 *p;
- int i;
+ int i, ret;
- clk_prepare_enable(dev->clk_per);
- clk_prepare_enable(dev->clk_ahb);
+ ret = clk_prepare_enable(dev->clk_per);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(dev->clk_ahb);
+ if (ret)
+ goto err_clk_ahb;
/*
* Copy the first CODA_ISRAM_SIZE in the internal SRAM.
@@ -1803,8 +2918,14 @@ static int coda_hw_init(struct coda_dev *dev)
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);
+ if (dev->devtype->product == CODA_7541) {
+ coda_write(dev, dev->tempbuf.paddr,
+ CODA_REG_BIT_TEMP_BUF_ADDR);
+ coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
+ } else {
+ coda_write(dev, dev->workbuf.paddr,
+ CODA_REG_BIT_WORK_BUF_ADDR);
+ }
coda_write(dev, dev->codebuf.paddr,
CODA_REG_BIT_CODE_BUF_ADDR);
coda_write(dev, 0, CODA_REG_BIT_CODE_RUN);
@@ -1877,6 +2998,10 @@ static int coda_hw_init(struct coda_dev *dev)
}
return 0;
+
+err_clk_ahb:
+ clk_disable_unprepare(dev->clk_per);
+ return ret;
}
static void coda_fw_callback(const struct firmware *fw, void *context)
@@ -1891,11 +3016,8 @@ static void coda_fw_callback(const struct firmware *fw, void *context)
}
/* allocate auxiliary per-device code buffer for the BIT processor */
- dev->codebuf.size = fw->size;
- dev->codebuf.vaddr = dma_alloc_coherent(&pdev->dev, fw->size,
- &dev->codebuf.paddr,
- GFP_KERNEL);
- if (!dev->codebuf.vaddr) {
+ ret = coda_alloc_aux_buf(dev, &dev->codebuf, fw->size);
+ if (ret < 0) {
dev_err(&pdev->dev, "failed to allocate code buffer\n");
return;
}
@@ -2032,11 +3154,6 @@ static int coda_probe(struct platform_device *pdev)
/* Get memory for physical registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
-
dev->regs_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(dev->regs_base))
return PTR_ERR(dev->regs_base);
@@ -2048,8 +3165,8 @@ static int coda_probe(struct platform_device *pdev)
return -ENOENT;
}
- if (devm_request_irq(&pdev->dev, irq, coda_irq_handler,
- 0, CODA_NAME, dev) < 0) {
+ if (devm_request_threaded_irq(&pdev->dev, irq, NULL, coda_irq_handler,
+ IRQF_ONESHOT, CODA_NAME, dev) < 0) {
dev_err(&pdev->dev, "failed to request irq\n");
return -ENOENT;
}
@@ -2085,24 +3202,36 @@ static int coda_probe(struct platform_device *pdev)
/* allocate auxiliary per-device buffers for the BIT processor */
switch (dev->devtype->product) {
case CODA_DX6:
- dev->workbuf.size = CODADX6_WORK_BUF_SIZE;
+ ret = coda_alloc_aux_buf(dev, &dev->workbuf,
+ CODADX6_WORK_BUF_SIZE);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate work buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
+ break;
+ case CODA_7541:
+ dev->tempbuf.size = CODA7_TEMP_BUF_SIZE;
break;
- default:
- dev->workbuf.size = CODA7_WORK_BUF_SIZE;
}
- dev->workbuf.vaddr = dma_alloc_coherent(&pdev->dev, dev->workbuf.size,
- &dev->workbuf.paddr,
- GFP_KERNEL);
- if (!dev->workbuf.vaddr) {
- dev_err(&pdev->dev, "failed to allocate work buffer\n");
- v4l2_device_unregister(&dev->v4l2_dev);
- return -ENOMEM;
+ if (dev->tempbuf.size) {
+ ret = coda_alloc_aux_buf(dev, &dev->tempbuf,
+ dev->tempbuf.size);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to allocate temp buffer\n");
+ v4l2_device_unregister(&dev->v4l2_dev);
+ return ret;
+ }
}
- if (dev->devtype->product == CODA_DX6)
+ switch (dev->devtype->product) {
+ case CODA_DX6:
dev->iram_size = CODADX6_IRAM_SIZE;
- else
+ break;
+ case CODA_7541:
dev->iram_size = CODA7_IRAM_SIZE;
+ break;
+ }
dev->iram_vaddr = gen_pool_alloc(dev->iram_pool, dev->iram_size);
if (!dev->iram_vaddr) {
dev_err(&pdev->dev, "unable to alloc iram\n");
@@ -2128,12 +3257,9 @@ static int coda_remove(struct platform_device *pdev)
v4l2_device_unregister(&dev->v4l2_dev);
if (dev->iram_vaddr)
gen_pool_free(dev->iram_pool, dev->iram_vaddr, dev->iram_size);
- if (dev->codebuf.vaddr)
- dma_free_coherent(&pdev->dev, dev->codebuf.size,
- &dev->codebuf.vaddr, dev->codebuf.paddr);
- if (dev->workbuf.vaddr)
- dma_free_coherent(&pdev->dev, dev->workbuf.size, &dev->workbuf.vaddr,
- dev->workbuf.paddr);
+ coda_free_aux_buf(dev, &dev->codebuf);
+ coda_free_aux_buf(dev, &dev->tempbuf);
+ coda_free_aux_buf(dev, &dev->workbuf);
return 0;
}
diff --git a/drivers/media/platform/coda.h b/drivers/media/platform/coda.h
index ace0bf0a3b9c..4e32e2edea62 100644
--- a/drivers/media/platform/coda.h
+++ b/drivers/media/platform/coda.h
@@ -43,14 +43,26 @@
#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
+#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114
+#define CODA_BIT_STREAM_END_FLAG (1 << 2)
+#define CODA_BIT_DEC_SEQ_INIT_ESCAPE (1 << 0)
+#define CODA_REG_BIT_TEMP_BUF_ADDR 0x118
#define CODA_REG_BIT_RD_PTR(x) (0x120 + 8 * (x))
#define CODA_REG_BIT_WR_PTR(x) (0x124 + 8 * (x))
+#define CODA_REG_BIT_FRM_DIS_FLG(x) (0x150 + 4 * (x))
#define CODADX6_REG_BIT_SEARCH_RAM_BASE_ADDR 0x140
#define CODA7_REG_BIT_AXI_SRAM_USE 0x140
-#define CODA7_USE_BIT_ENABLE (1 << 0)
+#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
+#define CODA7_USE_HOST_OVL_ENABLE (1 << 10)
+#define CODA7_USE_HOST_DBK_ENABLE (1 << 9)
+#define CODA7_USE_HOST_IP_ENABLE (1 << 8)
#define CODA7_USE_HOST_BIT_ENABLE (1 << 7)
#define CODA7_USE_ME_ENABLE (1 << 4)
-#define CODA7_USE_HOST_ME_ENABLE (1 << 11)
+#define CODA7_USE_OVL_ENABLE (1 << 3)
+#define CODA7_USE_DBK_ENABLE (1 << 2)
+#define CODA7_USE_IP_ENABLE (1 << 1)
+#define CODA7_USE_BIT_ENABLE (1 << 0)
+
#define CODA_REG_BIT_BUSY 0x160
#define CODA_REG_BIT_BUSY_FLAG 1
#define CODA_REG_BIT_RUN_COMMAND 0x164
@@ -84,6 +96,15 @@
#define CODA_MODE_INVALID 0xffff
#define CODA_REG_BIT_INT_ENABLE 0x170
#define CODA_INT_INTERRUPT_ENABLE (1 << 3)
+#define CODA_REG_BIT_INT_REASON 0x174
+#define CODA7_REG_BIT_RUN_AUX_STD 0x178
+#define CODA_MP4_AUX_MPEG4 0
+#define CODA_MP4_AUX_DIVX3 1
+#define CODA_VPX_AUX_THO 0
+#define CODA_VPX_AUX_VP6 1
+#define CODA_VPX_AUX_VP8 2
+#define CODA_H264_AUX_AVC 0
+#define CODA_H264_AUX_MVC 1
/*
* Commands' mailbox:
@@ -92,15 +113,89 @@
* issued.
*/
+/* Decoder Sequence Initialization */
+#define CODA_CMD_DEC_SEQ_BB_START 0x180
+#define CODA_CMD_DEC_SEQ_BB_SIZE 0x184
+#define CODA_CMD_DEC_SEQ_OPTION 0x188
+#define CODA_REORDER_ENABLE (1 << 1)
+#define CODADX6_QP_REPORT (1 << 0)
+#define CODA7_MP4_DEBLK_ENABLE (1 << 0)
+#define CODA_CMD_DEC_SEQ_SRC_SIZE 0x18c
+#define CODA_CMD_DEC_SEQ_START_BYTE 0x190
+#define CODA_CMD_DEC_SEQ_PS_BB_START 0x194
+#define CODA_CMD_DEC_SEQ_PS_BB_SIZE 0x198
+#define CODA_CMD_DEC_SEQ_MP4_ASP_CLASS 0x19c
+#define CODA_CMD_DEC_SEQ_X264_MV_EN 0x19c
+#define CODA_CMD_DEC_SEQ_SPP_CHUNK_SIZE 0x1a0
+
+#define CODA7_RET_DEC_SEQ_ASPECT 0x1b0
+#define CODA_RET_DEC_SEQ_SUCCESS 0x1c0
+#define CODA_RET_DEC_SEQ_SRC_FMT 0x1c4 /* SRC_SIZE on CODA7 */
+#define CODA_RET_DEC_SEQ_SRC_SIZE 0x1c4
+#define CODA_RET_DEC_SEQ_SRC_F_RATE 0x1c8
+#define CODA9_RET_DEC_SEQ_ASPECT 0x1c8
+#define CODA_RET_DEC_SEQ_FRAME_NEED 0x1cc
+#define CODA_RET_DEC_SEQ_FRAME_DELAY 0x1d0
+#define CODA_RET_DEC_SEQ_INFO 0x1d4
+#define CODA_RET_DEC_SEQ_CROP_LEFT_RIGHT 0x1d8
+#define CODA_RET_DEC_SEQ_CROP_TOP_BOTTOM 0x1dc
+#define CODA_RET_DEC_SEQ_NEXT_FRAME_NUM 0x1e0
+#define CODA_RET_DEC_SEQ_ERR_REASON 0x1e0
+#define CODA_RET_DEC_SEQ_FRATE_NR 0x1e4
+#define CODA_RET_DEC_SEQ_FRATE_DR 0x1e8
+#define CODA_RET_DEC_SEQ_JPG_PARA 0x1e4
+#define CODA_RET_DEC_SEQ_JPG_THUMB_IND 0x1e8
+
+/* Decoder Picture Run */
+#define CODA_CMD_DEC_PIC_ROT_MODE 0x180
+#define CODA_CMD_DEC_PIC_ROT_ADDR_Y 0x184
+#define CODA_CMD_DEC_PIC_ROT_ADDR_CB 0x188
+#define CODA_CMD_DEC_PIC_ROT_ADDR_CR 0x18c
+#define CODA_CMD_DEC_PIC_ROT_STRIDE 0x190
+
+#define CODA_CMD_DEC_PIC_OPTION 0x194
+#define CODA_PRE_SCAN_EN (1 << 0)
+#define CODA_PRE_SCAN_MODE_DECODE (0 << 1)
+#define CODA_PRE_SCAN_MODE_RETURN (1 << 1)
+#define CODA_IFRAME_SEARCH_EN (1 << 2)
+#define CODA_SKIP_FRAME_MODE (0x3 << 3)
+#define CODA_CMD_DEC_PIC_SKIP_NUM 0x198
+#define CODA_CMD_DEC_PIC_CHUNK_SIZE 0x19c
+#define CODA_CMD_DEC_PIC_BB_START 0x1a0
+#define CODA_CMD_DEC_PIC_START_BYTE 0x1a4
+#define CODA_RET_DEC_PIC_SIZE 0x1bc
+#define CODA_RET_DEC_PIC_FRAME_NUM 0x1c0
+#define CODA_RET_DEC_PIC_FRAME_IDX 0x1c4
+#define CODA_RET_DEC_PIC_ERR_MB 0x1c8
+#define CODA_RET_DEC_PIC_TYPE 0x1cc
+#define CODA_PIC_TYPE_MASK 0x7
+#define CODA_PIC_TYPE_MASK_VC1 0x3f
+#define CODA9_PIC_TYPE_FIRST_MASK (0x7 << 3)
+#define CODA9_PIC_TYPE_IDR_MASK (0x3 << 6)
+#define CODA7_PIC_TYPE_H264_NPF_MASK (0x3 << 16)
+#define CODA7_PIC_TYPE_INTERLACED (1 << 18)
+#define CODA_RET_DEC_PIC_POST 0x1d0
+#define CODA_RET_DEC_PIC_MVC_REPORT 0x1d0
+#define CODA_RET_DEC_PIC_OPTION 0x1d4
+#define CODA_RET_DEC_PIC_SUCCESS 0x1d8
+#define CODA_RET_DEC_PIC_CUR_IDX 0x1dc
+#define CODA_RET_DEC_PIC_CROP_LEFT_RIGHT 0x1e0
+#define CODA_RET_DEC_PIC_CROP_TOP_BOTTOM 0x1e4
+#define CODA_RET_DEC_PIC_FRAME_NEED 0x1ec
+
/* Encoder Sequence Initialization */
#define CODA_CMD_ENC_SEQ_BB_START 0x180
#define CODA_CMD_ENC_SEQ_BB_SIZE 0x184
#define CODA_CMD_ENC_SEQ_OPTION 0x188
+#define CODA7_OPTION_AVCINTRA16X16ONLY_OFFSET 9
#define CODA7_OPTION_GAMMA_OFFSET 8
+#define CODA7_OPTION_RCQPMAX_OFFSET 7
#define CODADX6_OPTION_GAMMA_OFFSET 7
+#define CODA7_OPTION_RCQPMIN_OFFSET 6
#define CODA_OPTION_LIMITQP_OFFSET 6
#define CODA_OPTION_RCINTRAQP_OFFSET 5
#define CODA_OPTION_FMO_OFFSET 4
+#define CODA_OPTION_AVC_AUD_OFFSET 2
#define CODA_OPTION_SLICEREPORT_OFFSET 1
#define CODA_CMD_ENC_SEQ_COD_STD 0x18c
#define CODA_STD_MPEG4 0
@@ -169,8 +264,10 @@
#define CODA_FMOPARAM_TYPE_MASK 1
#define CODA_FMOPARAM_SLICENUM_OFFSET 0
#define CODA_FMOPARAM_SLICENUM_MASK 0x0f
+#define CODADX6_CMD_ENC_SEQ_INTRA_QP 0x1bc
#define CODA7_CMD_ENC_SEQ_SEARCH_BASE 0x1b8
#define CODA7_CMD_ENC_SEQ_SEARCH_SIZE 0x1bc
+#define CODA7_CMD_ENC_SEQ_INTRA_QP 0x1c4
#define CODA_CMD_ENC_SEQ_RC_QP_MAX 0x1c8
#define CODA_QPMAX_OFFSET 0
#define CODA_QPMAX_MASK 0x3f
@@ -197,18 +294,24 @@
#define CODA_CMD_ENC_PIC_OPTION 0x194
#define CODA_CMD_ENC_PIC_BB_START 0x198
#define CODA_CMD_ENC_PIC_BB_SIZE 0x19c
+#define CODA_RET_ENC_FRAME_NUM 0x1c0
#define CODA_RET_ENC_PIC_TYPE 0x1c4
+#define CODA_RET_ENC_PIC_FRAME_IDX 0x1c8
#define CODA_RET_ENC_PIC_SLICE_NUM 0x1cc
#define CODA_RET_ENC_PIC_FLAG 0x1d0
+#define CODA_RET_ENC_PIC_SUCCESS 0x1d8
/* Set Frame Buffer */
#define CODA_CMD_SET_FRAME_BUF_NUM 0x180
#define CODA_CMD_SET_FRAME_BUF_STRIDE 0x184
+#define CODA_CMD_SET_FRAME_SLICE_BB_START 0x188
+#define CODA_CMD_SET_FRAME_SLICE_BB_SIZE 0x18c
#define CODA7_CMD_SET_FRAME_AXI_BIT_ADDR 0x190
#define CODA7_CMD_SET_FRAME_AXI_IPACDC_ADDR 0x194
#define CODA7_CMD_SET_FRAME_AXI_DBKY_ADDR 0x198
#define CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR 0x19c
#define CODA7_CMD_SET_FRAME_AXI_OVL_ADDR 0x1a0
+#define CODA7_CMD_SET_FRAME_MAX_DEC_SIZE 0x1a4
#define CODA7_CMD_SET_FRAME_SOURCE_BUF_STRIDE 0x1a8
/* Encoder Header */
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/davinci/vpbe_display.c
index e180ff7282d9..04609cc6eba7 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/davinci/vpbe_display.c
@@ -1743,11 +1743,10 @@ static int vpbe_display_probe(struct platform_device *pdev)
printk(KERN_DEBUG "vpbe_display_probe\n");
/* Allocate memory for vpbe_display */
- disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL);
- if (!disp_dev) {
- printk(KERN_ERR "ran out of memory\n");
+ disp_dev = devm_kzalloc(&pdev->dev, sizeof(struct vpbe_display),
+ GFP_KERNEL);
+ if (!disp_dev)
return -ENOMEM;
- }
spin_lock_init(&disp_dev->dma_queue_lock);
/*
@@ -1786,26 +1785,24 @@ static int vpbe_display_probe(struct platform_device *pdev)
}
irq = res->start;
- if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER,
- disp_dev)) {
+ err = devm_request_irq(&pdev->dev, irq, venc_isr, IRQF_DISABLED,
+ VPBE_DISPLAY_DRIVER, disp_dev);
+ if (err) {
v4l2_err(&disp_dev->vpbe_dev->v4l2_dev,
"Unable to request interrupt\n");
- err = -ENODEV;
goto probe_out;
}
for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) {
if (register_device(disp_dev->dev[i], disp_dev, pdev)) {
err = -ENODEV;
- goto probe_out_irq;
+ goto probe_out;
}
}
printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n");
return 0;
-probe_out_irq:
- free_irq(res->start, disp_dev);
probe_out:
for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) {
/* Get the pointer to the layer object */
@@ -1817,7 +1814,6 @@ probe_out:
kfree(disp_dev->dev[k]);
}
}
- kfree(disp_dev);
return err;
}
@@ -1830,15 +1826,10 @@ static int vpbe_display_remove(struct platform_device *pdev)
struct vpbe_layer *vpbe_display_layer;
struct vpbe_display *disp_dev = platform_get_drvdata(pdev);
struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev;
- struct resource *res;
int i;
v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n");
- /* unregister irq */
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- free_irq(res->start, disp_dev);
-
/* deinitialize the vpbe display controller */
if (NULL != vpbe_dev->ops.deinitialize)
vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev);
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/davinci/vpbe_osd.c
index 6ed82e8b297b..d053c2669c1f 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/davinci/vpbe_osd.c
@@ -1547,61 +1547,36 @@ static int osd_probe(struct platform_device *pdev)
const struct platform_device_id *pdev_id;
struct osd_state *osd;
struct resource *res;
- int ret = 0;
- osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL);
+ pdev_id = platform_get_device_id(pdev);
+ if (!pdev_id)
+ return -EINVAL;
+
+ osd = devm_kzalloc(&pdev->dev, sizeof(struct osd_state), GFP_KERNEL);
if (osd == NULL)
return -ENOMEM;
- pdev_id = platform_get_device_id(pdev);
- if (!pdev_id) {
- ret = -EINVAL;
- goto free_mem;
- }
osd->dev = &pdev->dev;
osd->vpbe_type = pdev_id->driver_data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(osd->dev, "Unable to get OSD register address map\n");
- ret = -ENODEV;
- goto free_mem;
- }
+ osd->osd_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(osd->osd_base))
+ return PTR_ERR(osd->osd_base);
+
osd->osd_base_phys = res->start;
osd->osd_size = resource_size(res);
- if (!request_mem_region(osd->osd_base_phys, osd->osd_size,
- MODULE_NAME)) {
- dev_err(osd->dev, "Unable to reserve OSD MMIO region\n");
- ret = -ENODEV;
- goto free_mem;
- }
- osd->osd_base = ioremap_nocache(res->start, osd->osd_size);
- if (!osd->osd_base) {
- dev_err(osd->dev, "Unable to map the OSD region\n");
- ret = -ENODEV;
- goto release_mem_region;
- }
spin_lock_init(&osd->lock);
osd->ops = osd_ops;
platform_set_drvdata(pdev, osd);
dev_notice(osd->dev, "OSD sub device probe success\n");
- return ret;
-release_mem_region:
- release_mem_region(osd->osd_base_phys, osd->osd_size);
-free_mem:
- kfree(osd);
- return ret;
+ return 0;
}
static int osd_remove(struct platform_device *pdev)
{
- struct osd_state *osd = platform_get_drvdata(pdev);
-
- iounmap((void *)osd->osd_base);
- release_mem_region(osd->osd_base_phys, osd->osd_size);
- kfree(osd);
return 0;
}
diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/davinci/vpbe_venc.c
index 87eef9be08ed..14a023a75d2d 100644
--- a/drivers/media/platform/davinci/vpbe_venc.c
+++ b/drivers/media/platform/davinci/vpbe_venc.c
@@ -639,105 +639,46 @@ static int venc_probe(struct platform_device *pdev)
const struct platform_device_id *pdev_id;
struct venc_state *venc;
struct resource *res;
- int ret;
- venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL);
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "No platform data for VENC sub device");
+ return -EINVAL;
+ }
+
+ pdev_id = platform_get_device_id(pdev);
+ if (!pdev_id)
+ return -EINVAL;
+
+ venc = devm_kzalloc(&pdev->dev, sizeof(struct venc_state), GFP_KERNEL);
if (venc == NULL)
return -ENOMEM;
- pdev_id = platform_get_device_id(pdev);
- if (!pdev_id) {
- ret = -EINVAL;
- goto free_mem;
- }
venc->venc_type = pdev_id->driver_data;
venc->pdev = &pdev->dev;
venc->pdata = pdev->dev.platform_data;
- if (NULL == venc->pdata) {
- dev_err(venc->pdev, "Unable to get platform data for"
- " VENC sub device");
- ret = -ENOENT;
- goto free_mem;
- }
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(venc->pdev,
- "Unable to get VENC register address map\n");
- ret = -ENODEV;
- goto free_mem;
- }
- if (!request_mem_region(res->start, resource_size(res), "venc")) {
- dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n");
- ret = -ENODEV;
- goto free_mem;
- }
-
- venc->venc_base = ioremap_nocache(res->start, resource_size(res));
- if (!venc->venc_base) {
- dev_err(venc->pdev, "Unable to map VENC IO space\n");
- ret = -ENODEV;
- goto release_venc_mem_region;
- }
+ venc->venc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(venc->venc_base))
+ return PTR_ERR(venc->venc_base);
if (venc->venc_type != VPBE_VERSION_1) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- dev_err(venc->pdev,
- "Unable to get VDAC_CONFIG address map\n");
- ret = -ENODEV;
- goto unmap_venc_io;
- }
-
- if (!request_mem_region(res->start,
- resource_size(res), "venc")) {
- dev_err(venc->pdev,
- "Unable to reserve VDAC_CONFIG MMIO region\n");
- ret = -ENODEV;
- goto unmap_venc_io;
- }
-
- venc->vdaccfg_reg = ioremap_nocache(res->start,
- resource_size(res));
- if (!venc->vdaccfg_reg) {
- dev_err(venc->pdev,
- "Unable to map VDAC_CONFIG IO space\n");
- ret = -ENODEV;
- goto release_vdaccfg_mem_region;
- }
+
+ venc->vdaccfg_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(venc->vdaccfg_reg))
+ return PTR_ERR(venc->vdaccfg_reg);
}
spin_lock_init(&venc->lock);
platform_set_drvdata(pdev, venc);
dev_notice(venc->pdev, "VENC sub device probe success\n");
- return 0;
-release_vdaccfg_mem_region:
- release_mem_region(res->start, resource_size(res));
-unmap_venc_io:
- iounmap(venc->venc_base);
-release_venc_mem_region:
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
-free_mem:
- kfree(venc);
- return ret;
+ return 0;
}
static int venc_remove(struct platform_device *pdev)
{
- struct venc_state *venc = platform_get_drvdata(pdev);
- struct resource *res;
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- iounmap((void *)venc->venc_base);
- release_mem_region(res->start, resource_size(res));
- if (venc->venc_type != VPBE_VERSION_1) {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- iounmap((void *)venc->vdaccfg_reg);
- release_mem_region(res->start, resource_size(res));
- }
- kfree(venc);
-
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c
index 5514175bbd07..1089834a4efe 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/davinci/vpif_capture.c
@@ -1799,19 +1799,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
+ std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
+ std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@@ -1825,8 +1821,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120", VPIF_MAX_NAME);
@@ -1979,6 +1973,76 @@ vpif_init_free_channel_objects:
return err;
}
+static int vpif_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ int i;
+
+ for (i = 0; i < vpif_obj.config->subdev_count; i++)
+ if (!strcmp(vpif_obj.config->subdev_info[i].name,
+ subdev->name)) {
+ vpif_obj.sd[i] = subdev;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vpif_probe_complete(void)
+{
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int i, j, err, k;
+
+ for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ ch->channel_id = j;
+ common = &(ch->common[VPIF_VIDEO_INDEX]);
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ ch->video_dev->lock = &common->lock;
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ video_set_drvdata(ch->video_dev, ch);
+
+ /* select input 0 */
+ err = vpif_set_input(vpif_obj.config, ch, 0);
+ if (err)
+ goto probe_out;
+
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 1 : 0));
+ if (err)
+ goto probe_out;
+ }
+
+ v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
+ return 0;
+
+probe_out:
+ for (k = 0; k < j; k++) {
+ /* Get the pointer to the channel object */
+ ch = vpif_obj.dev[k];
+ /* Unregister video device */
+ video_unregister_device(ch->video_dev);
+ }
+ kfree(vpif_obj.sd);
+ for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) {
+ ch = vpif_obj.dev[i];
+ /* Note: does nothing if ch->video_dev == NULL */
+ video_device_release(ch->video_dev);
+ }
+ v4l2_device_unregister(&vpif_obj.v4l2_dev);
+
+ return err;
+}
+
+static int vpif_async_complete(struct v4l2_async_notifier *notifier)
+{
+ return vpif_probe_complete();
+}
+
/**
* vpif_probe : This function probes the vpif capture driver
* @pdev: platform device pointer
@@ -1989,12 +2053,10 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct vpif_capture_config *config;
- int i, j, k, err;
+ int i, j, err;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
struct channel_obj *ch;
- struct common_obj *common;
struct video_device *vfd;
struct resource *res;
int subdev_count;
@@ -2068,10 +2130,9 @@ static __init int vpif_probe(struct platform_device *pdev)
}
}
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
+ vpif_obj.config = pdev->dev.platform_data;
- subdev_count = config->subdev_count;
+ subdev_count = vpif_obj.config->subdev_count;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@@ -2080,54 +2141,43 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
- for (i = 0; i < subdev_count; i++) {
- subdevdata = &config->subdev_info[i];
- vpif_obj.sd[i] =
- v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata->board_info,
- NULL);
-
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- err = -ENODEV;
+ if (!vpif_obj.config->asd_sizes) {
+ i2c_adap = i2c_get_adapter(1);
+ for (i = 0; i < subdev_count; i++) {
+ subdevdata = &vpif_obj.config->subdev_info[i];
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata->
+ board_info,
+ NULL);
+
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENOMEM;
+ goto probe_subdev_out;
+ }
+ v4l2_info(&vpif_obj.v4l2_dev,
+ "registered sub device %s\n",
+ subdevdata->name);
+ }
+ vpif_probe_complete();
+ } else {
+ vpif_obj.notifier.subdevs = vpif_obj.config->asd;
+ vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
+ vpif_obj.notifier.bound = vpif_async_bound;
+ vpif_obj.notifier.complete = vpif_async_complete;
+ err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
+ &vpif_obj.notifier);
+ if (err) {
+ vpif_err("Error registering async notifier\n");
+ err = -EINVAL;
goto probe_subdev_out;
}
- v4l2_info(&vpif_obj.v4l2_dev, "registered sub device %s\n",
- subdevdata->name);
}
- for (j = 0; j < VPIF_CAPTURE_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- ch->channel_id = j;
- common = &(ch->common[VPIF_VIDEO_INDEX]);
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- ch->video_dev->lock = &common->lock;
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- video_set_drvdata(ch->video_dev, ch);
-
- /* select input 0 */
- err = vpif_set_input(config, ch, 0);
- if (err)
- goto probe_out;
-
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 1 : 0));
- if (err)
- goto probe_out;
- }
- v4l2_info(&vpif_obj.v4l2_dev, "VPIF capture driver initialized\n");
return 0;
-probe_out:
- for (k = 0; k < j; k++) {
- /* Get the pointer to the channel object */
- ch = vpif_obj.dev[k];
- /* Unregister video device */
- video_unregister_device(ch->video_dev);
- }
probe_subdev_out:
/* free sub devices memory */
kfree(vpif_obj.sd);
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/davinci/vpif_capture.h
index 0ebb31260369..5a29d9a0cae1 100644
--- a/drivers/media/platform/davinci/vpif_capture.h
+++ b/drivers/media/platform/davinci/vpif_capture.h
@@ -142,6 +142,8 @@ struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_CAPTURE_NUM_CHANNELS];
struct v4l2_subdev **sd;
+ struct v4l2_async_notifier notifier;
+ struct vpif_capture_config *config;
};
struct vpif_config_params {
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c
index e6e573650250..c31bcf129a5d 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/davinci/vpif_display.c
@@ -1436,19 +1436,15 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
/* Configure video port timings */
- std_info->eav2sav = bt->hbackporch + bt->hfrontporch +
- bt->hsync - 8;
+ std_info->eav2sav = V4L2_DV_BT_BLANKING_WIDTH(bt) - 8;
std_info->sav2eav = bt->width;
std_info->l1 = 1;
std_info->l3 = bt->vsync + bt->vbackporch + 1;
+ std_info->vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
if (bt->interlaced) {
if (bt->il_vbackporch || bt->il_vfrontporch || bt->il_vsync) {
- std_info->vsize = bt->height * 2 +
- bt->vfrontporch + bt->vsync + bt->vbackporch +
- bt->il_vfrontporch + bt->il_vsync +
- bt->il_vbackporch;
std_info->l5 = std_info->vsize/2 -
(bt->vfrontporch - 1);
std_info->l7 = std_info->vsize/2 + 1;
@@ -1462,8 +1458,6 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EINVAL;
}
} else {
- std_info->vsize = bt->height + bt->vfrontporch +
- bt->vsync + bt->vbackporch;
std_info->l5 = std_info->vsize - (bt->vfrontporch - 1);
}
strncpy(std_info->name, "Custom timings BT656/1120",
@@ -1618,6 +1612,102 @@ vpif_init_free_channel_objects:
return err;
}
+static int vpif_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ int i;
+
+ for (i = 0; i < vpif_obj.config->subdev_count; i++)
+ if (!strcmp(vpif_obj.config->subdevinfo[i].name,
+ subdev->name)) {
+ vpif_obj.sd[i] = subdev;
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int vpif_probe_complete(void)
+{
+ struct common_obj *common;
+ struct channel_obj *ch;
+ int j, err, k;
+
+ for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
+ ch = vpif_obj.dev[j];
+ /* Initialize field of the channel objects */
+ atomic_set(&ch->usrs, 0);
+ for (k = 0; k < VPIF_NUMOBJECTS; k++) {
+ ch->common[k].numbuffers = 0;
+ common = &ch->common[k];
+ common->io_usrs = 0;
+ common->started = 0;
+ spin_lock_init(&common->irqlock);
+ mutex_init(&common->lock);
+ common->numbuffers = 0;
+ common->set_addr = NULL;
+ common->ytop_off = 0;
+ common->ybtm_off = 0;
+ common->ctop_off = 0;
+ common->cbtm_off = 0;
+ common->cur_frm = NULL;
+ common->next_frm = NULL;
+ memset(&common->fmt, 0, sizeof(common->fmt));
+ common->numbuffers = config_params.numbuffers[k];
+ }
+ ch->initialized = 0;
+ if (vpif_obj.config->subdev_count)
+ ch->sd = vpif_obj.sd[0];
+ ch->channel_id = j;
+ if (j < 2)
+ ch->common[VPIF_VIDEO_INDEX].numbuffers =
+ config_params.numbuffers[ch->channel_id];
+ else
+ ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
+
+ memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
+
+ /* Initialize prio member of channel object */
+ v4l2_prio_init(&ch->prio);
+ ch->common[VPIF_VIDEO_INDEX].fmt.type =
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ ch->video_dev->lock = &common->lock;
+ video_set_drvdata(ch->video_dev, ch);
+
+ /* select output 0 */
+ err = vpif_set_output(vpif_obj.config, ch, 0);
+ if (err)
+ goto probe_out;
+
+ /* register video device */
+ vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
+ (int)ch, (int)&ch->video_dev);
+
+ err = video_register_device(ch->video_dev,
+ VFL_TYPE_GRABBER, (j ? 3 : 2));
+ if (err < 0)
+ goto probe_out;
+ }
+
+ return 0;
+
+probe_out:
+ for (k = 0; k < j; k++) {
+ ch = vpif_obj.dev[k];
+ video_unregister_device(ch->video_dev);
+ video_device_release(ch->video_dev);
+ ch->video_dev = NULL;
+ }
+ return err;
+}
+
+static int vpif_async_complete(struct v4l2_async_notifier *notifier)
+{
+ return vpif_probe_complete();
+}
+
/*
* vpif_probe: This function creates device entries by register itself to the
* V4L2 driver and initializes fields of each channel objects
@@ -1625,11 +1715,9 @@ vpif_init_free_channel_objects:
static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct vpif_display_config *config;
- int i, j = 0, k, err = 0;
+ int i, j = 0, err = 0;
int res_idx = 0;
struct i2c_adapter *i2c_adap;
- struct common_obj *common;
struct channel_obj *ch;
struct video_device *vfd;
struct resource *res;
@@ -1708,11 +1796,9 @@ static __init int vpif_probe(struct platform_device *pdev)
size/2;
}
}
-
- i2c_adap = i2c_get_adapter(1);
- config = pdev->dev.platform_data;
- subdev_count = config->subdev_count;
- subdevdata = config->subdevinfo;
+ vpif_obj.config = pdev->dev.platform_data;
+ subdev_count = vpif_obj.config->subdev_count;
+ subdevdata = vpif_obj.config->subdevinfo;
vpif_obj.sd = kzalloc(sizeof(struct v4l2_subdev *) * subdev_count,
GFP_KERNEL);
if (vpif_obj.sd == NULL) {
@@ -1721,86 +1807,41 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_sd_error;
}
- for (i = 0; i < subdev_count; i++) {
- vpif_obj.sd[i] = v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
- i2c_adap,
- &subdevdata[i].board_info,
- NULL);
- if (!vpif_obj.sd[i]) {
- vpif_err("Error registering v4l2 subdevice\n");
- err = -ENODEV;
- goto probe_subdev_out;
- }
-
- if (vpif_obj.sd[i])
- vpif_obj.sd[i]->grp_id = 1 << i;
- }
-
- for (j = 0; j < VPIF_DISPLAY_MAX_DEVICES; j++) {
- ch = vpif_obj.dev[j];
- /* Initialize field of the channel objects */
- atomic_set(&ch->usrs, 0);
- for (k = 0; k < VPIF_NUMOBJECTS; k++) {
- ch->common[k].numbuffers = 0;
- common = &ch->common[k];
- common->io_usrs = 0;
- common->started = 0;
- spin_lock_init(&common->irqlock);
- mutex_init(&common->lock);
- common->numbuffers = 0;
- common->set_addr = NULL;
- common->ytop_off = common->ybtm_off = 0;
- common->ctop_off = common->cbtm_off = 0;
- common->cur_frm = common->next_frm = NULL;
- memset(&common->fmt, 0, sizeof(common->fmt));
- common->numbuffers = config_params.numbuffers[k];
+ if (!vpif_obj.config->asd_sizes) {
+ i2c_adap = i2c_get_adapter(1);
+ for (i = 0; i < subdev_count; i++) {
+ vpif_obj.sd[i] =
+ v4l2_i2c_new_subdev_board(&vpif_obj.v4l2_dev,
+ i2c_adap,
+ &subdevdata[i].
+ board_info,
+ NULL);
+ if (!vpif_obj.sd[i]) {
+ vpif_err("Error registering v4l2 subdevice\n");
+ err = -ENODEV;
+ goto probe_subdev_out;
+ }
+ if (vpif_obj.sd[i])
+ vpif_obj.sd[i]->grp_id = 1 << i;
+ }
+ vpif_probe_complete();
+ } else {
+ vpif_obj.notifier.subdevs = vpif_obj.config->asd;
+ vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0];
+ vpif_obj.notifier.bound = vpif_async_bound;
+ vpif_obj.notifier.complete = vpif_async_complete;
+ err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev,
+ &vpif_obj.notifier);
+ if (err) {
+ vpif_err("Error registering async notifier\n");
+ err = -EINVAL;
+ goto probe_subdev_out;
}
- ch->initialized = 0;
- if (subdev_count)
- ch->sd = vpif_obj.sd[0];
- ch->channel_id = j;
- if (j < 2)
- ch->common[VPIF_VIDEO_INDEX].numbuffers =
- config_params.numbuffers[ch->channel_id];
- else
- ch->common[VPIF_VIDEO_INDEX].numbuffers = 0;
-
- memset(&ch->vpifparams, 0, sizeof(ch->vpifparams));
-
- /* Initialize prio member of channel object */
- v4l2_prio_init(&ch->prio);
- ch->common[VPIF_VIDEO_INDEX].fmt.type =
- V4L2_BUF_TYPE_VIDEO_OUTPUT;
- ch->video_dev->lock = &common->lock;
- video_set_drvdata(ch->video_dev, ch);
-
- /* select output 0 */
- err = vpif_set_output(config, ch, 0);
- if (err)
- goto probe_out;
-
- /* register video device */
- vpif_dbg(1, debug, "channel=%x,channel->video_dev=%x\n",
- (int)ch, (int)&ch->video_dev);
-
- err = video_register_device(ch->video_dev,
- VFL_TYPE_GRABBER, (j ? 3 : 2));
- if (err < 0)
- goto probe_out;
}
- v4l2_info(&vpif_obj.v4l2_dev,
- " VPIF display driver initialized\n");
return 0;
-probe_out:
- for (k = 0; k < j; k++) {
- ch = vpif_obj.dev[k];
- video_unregister_device(ch->video_dev);
- video_device_release(ch->video_dev);
- ch->video_dev = NULL;
- }
probe_subdev_out:
kfree(vpif_obj.sd);
vpif_sd_error:
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/davinci/vpif_display.h
index 5d87fc86e580..4d0485b99a80 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/davinci/vpif_display.h
@@ -148,7 +148,8 @@ struct vpif_device {
struct v4l2_device v4l2_dev;
struct channel_obj *dev[VPIF_DISPLAY_NUM_CHANNELS];
struct v4l2_subdev **sd;
-
+ struct v4l2_async_notifier notifier;
+ struct vpif_display_config *config;
};
struct vpif_config_params {
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/davinci/vpss.c
index 8a2f01e344ee..31120b4a4a33 100644
--- a/drivers/media/platform/davinci/vpss.c
+++ b/drivers/media/platform/davinci/vpss.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/err.h>
#include <media/davinci/vpss.h>
@@ -404,9 +405,8 @@ EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size);
static int vpss_probe(struct platform_device *pdev)
{
- struct resource *r1, *r2;
+ struct resource *res;
char *platform_name;
- int status;
if (!pdev->dev.platform_data) {
dev_err(&pdev->dev, "no platform data\n");
@@ -427,38 +427,19 @@ static int vpss_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "%s vpss probed\n", platform_name);
- r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r1)
- return -ENOENT;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- r1 = request_mem_region(r1->start, resource_size(r1), r1->name);
- if (!r1)
- return -EBUSY;
-
- oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1));
- if (!oper_cfg.vpss_regs_base0) {
- status = -EBUSY;
- goto fail1;
- }
+ oper_cfg.vpss_regs_base0 = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(oper_cfg.vpss_regs_base0))
+ return PTR_ERR(oper_cfg.vpss_regs_base0);
if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!r2) {
- status = -ENOENT;
- goto fail2;
- }
- r2 = request_mem_region(r2->start, resource_size(r2), r2->name);
- if (!r2) {
- status = -EBUSY;
- goto fail2;
- }
-
- oper_cfg.vpss_regs_base1 = ioremap(r2->start,
- resource_size(r2));
- if (!oper_cfg.vpss_regs_base1) {
- status = -EBUSY;
- goto fail3;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ oper_cfg.vpss_regs_base1 = devm_ioremap_resource(&pdev->dev,
+ res);
+ if (IS_ERR(oper_cfg.vpss_regs_base1))
+ return PTR_ERR(oper_cfg.vpss_regs_base1);
}
if (oper_cfg.platform == DM355) {
@@ -493,30 +474,13 @@ static int vpss_probe(struct platform_device *pdev)
spin_lock_init(&oper_cfg.vpss_lock);
dev_info(&pdev->dev, "%s vpss probe success\n", platform_name);
- return 0;
-fail3:
- release_mem_region(r2->start, resource_size(r2));
-fail2:
- iounmap(oper_cfg.vpss_regs_base0);
-fail1:
- release_mem_region(r1->start, resource_size(r1));
- return status;
+ return 0;
}
static int vpss_remove(struct platform_device *pdev)
{
- struct resource *res;
-
pm_runtime_disable(&pdev->dev);
- iounmap(oper_cfg.vpss_regs_base0);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, resource_size(res));
- if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) {
- iounmap(oper_cfg.vpss_regs_base1);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- release_mem_region(res->start, resource_size(res));
- }
return 0;
}
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 559fab2a2d67..9d0cc04d7ab7 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -1122,10 +1122,14 @@ static int gsc_probe(struct platform_device *pdev)
goto err_clk;
}
- ret = gsc_register_m2m_device(gsc);
+ ret = v4l2_device_register(dev, &gsc->v4l2_dev);
if (ret)
goto err_clk;
+ ret = gsc_register_m2m_device(gsc);
+ if (ret)
+ goto err_v4l2;
+
platform_set_drvdata(pdev, gsc);
pm_runtime_enable(dev);
ret = pm_runtime_get_sync(&pdev->dev);
@@ -1147,6 +1151,8 @@ err_pm:
pm_runtime_put(dev);
err_m2m:
gsc_unregister_m2m_device(gsc);
+err_v4l2:
+ v4l2_device_unregister(&gsc->v4l2_dev);
err_clk:
gsc_clk_put(gsc);
return ret;
@@ -1157,6 +1163,7 @@ static int gsc_remove(struct platform_device *pdev)
struct gsc_dev *gsc = platform_get_drvdata(pdev);
gsc_unregister_m2m_device(gsc);
+ v4l2_device_unregister(&gsc->v4l2_dev);
vb2_dma_contig_cleanup_ctx(gsc->alloc_ctx);
pm_runtime_disable(&pdev->dev);
@@ -1210,12 +1217,12 @@ static int gsc_resume(struct device *dev)
spin_unlock_irqrestore(&gsc->slock, flags);
return 0;
}
- gsc_hw_set_sw_reset(gsc);
- gsc_wait_reset(gsc);
-
spin_unlock_irqrestore(&gsc->slock, flags);
- return gsc_m2m_resume(gsc);
+ if (!pm_runtime_suspended(dev))
+ return gsc_runtime_resume(dev);
+
+ return 0;
}
static int gsc_suspend(struct device *dev)
@@ -1227,7 +1234,10 @@ static int gsc_suspend(struct device *dev)
if (test_and_set_bit(ST_SUSPEND, &gsc->state))
return 0;
- return gsc_m2m_suspend(gsc);
+ if (!pm_runtime_suspended(dev))
+ return gsc_runtime_suspend(dev);
+
+ return 0;
}
static const struct dev_pm_ops gsc_pm_ops = {
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h
index cc19bba09bd1..76435d3bf62d 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/exynos-gsc/gsc-core.h
@@ -343,6 +343,7 @@ struct gsc_dev {
unsigned long state;
struct vb2_alloc_ctx *alloc_ctx;
struct video_device vdev;
+ struct v4l2_device v4l2_dev;
};
/**
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c
index 40a73f7d20da..e576ff2de3de 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c
@@ -751,6 +751,7 @@ int gsc_register_m2m_device(struct gsc_dev *gsc)
gsc->vdev.release = video_device_release_empty;
gsc->vdev.lock = &gsc->lock;
gsc->vdev.vfl_dir = VFL_DIR_M2M;
+ gsc->vdev.v4l2_dev = &gsc->v4l2_dev;
snprintf(gsc->vdev.name, sizeof(gsc->vdev.name), "%s.%d:m2m",
GSC_MODULE_NAME, gsc->id);
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/exynos4-is/fimc-core.c
index 6489c5160ee8..3d66d88ea3a1 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/exynos4-is/fimc-core.c
@@ -1110,6 +1110,8 @@ static int fimc_remove(struct platform_device *pdev)
struct fimc_dev *fimc = platform_get_drvdata(pdev);
pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ clk_disable(fimc->clock[CLK_GATE]);
pm_runtime_set_suspended(&pdev->dev);
fimc_unregister_capture_subdev(fimc);
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
index 993055605214..371cad4fcce9 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-i2c.c
@@ -81,21 +81,46 @@ static int fimc_is_i2c_remove(struct platform_device *pdev)
return 0;
}
-static int fimc_is_i2c_suspend(struct device *dev)
+#if defined(CONFIG_PM_RUNTIME) || defined(CONFIG_PM_SLEEP)
+static int fimc_is_i2c_runtime_suspend(struct device *dev)
{
struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
+
clk_disable_unprepare(isp_i2c->clock);
return 0;
}
-static int fimc_is_i2c_resume(struct device *dev)
+static int fimc_is_i2c_runtime_resume(struct device *dev)
{
struct fimc_is_i2c *isp_i2c = dev_get_drvdata(dev);
+
return clk_prepare_enable(isp_i2c->clock);
}
+#endif
-static UNIVERSAL_DEV_PM_OPS(fimc_is_i2c_pm_ops, fimc_is_i2c_suspend,
- fimc_is_i2c_resume, NULL);
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_i2c_suspend(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return fimc_is_i2c_runtime_suspend(dev);
+}
+
+static int fimc_is_i2c_resume(struct device *dev)
+{
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ return fimc_is_i2c_runtime_resume(dev);
+}
+#endif
+
+static struct dev_pm_ops fimc_is_i2c_pm_ops = {
+ SET_RUNTIME_PM_OPS(fimc_is_i2c_runtime_suspend,
+ fimc_is_i2c_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fimc_is_i2c_suspend, fimc_is_i2c_resume)
+};
static const struct of_device_id fimc_is_i2c_of_match[] = {
{ .compatible = FIMC_IS_I2C_COMPATIBLE },
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/exynos4-is/fimc-is-param.c
index c7e7f694c6ed..9bf3ddd9e028 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-param.c
@@ -56,7 +56,7 @@ static void __fimc_is_hw_update_param_sensor_framerate(struct fimc_is *is)
__hw_param_copy(dst, src);
}
-int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
+static int __fimc_is_hw_update_param(struct fimc_is *is, u32 offset)
{
struct is_param_region *par = &is->is_p_region->parameter;
struct chain_config *cfg = &is->config[is->config_index];
@@ -287,7 +287,7 @@ void __is_set_sensor(struct fimc_is *is, int fps)
fimc_is_set_param_bit(is, PARAM_ISP_OTF_INPUT);
}
-void __is_set_init_isp_aa(struct fimc_is *is)
+static void __maybe_unused __is_set_init_isp_aa(struct fimc_is *is)
{
struct isp_param *isp;
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/exynos4-is/fimc-is-regs.c
index 63c68ec7cfa4..f758e2694fa3 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/exynos4-is/fimc-is-regs.c
@@ -96,7 +96,7 @@ int fimc_is_hw_set_param(struct fimc_is *is)
return 0;
}
-int fimc_is_hw_set_tune(struct fimc_is *is)
+static int __maybe_unused fimc_is_hw_set_tune(struct fimc_is *is)
{
fimc_is_hw_wait_intmsr0_intmsd0(is);
@@ -236,7 +236,7 @@ int fimc_is_itf_mode_change(struct fimc_is *is)
fimc_is_hw_change_mode(is);
ret = fimc_is_wait_event(is, IS_ST_CHANGE_MODE, 1,
FIMC_IS_CONFIG_TIMEOUT);
- if (!ret < 0)
+ if (ret < 0)
dev_err(&is->pdev->dev, "%s(): mode change (%d) timeout\n",
__func__, is->config_index);
return ret;
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 2276fdc213c5..9770fa98d6a1 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -993,3 +993,4 @@ module_exit(fimc_is_module_exit);
MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/exynos4-is/fimc-isp.c
index cf520a7d7f71..d2e6cba3566d 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/exynos4-is/fimc-isp.c
@@ -672,6 +672,8 @@ int fimc_isp_subdev_create(struct fimc_isp *isp)
mutex_init(&isp->subdev_lock);
v4l2_subdev_init(sd, &fimc_is_subdev_ops);
+
+ sd->owner = THIS_MODULE;
sd->grp_id = GRP_ID_FIMC_IS;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "FIMC-IS-ISP");
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/exynos4-is/fimc-lite.c
index 08fbfedea90f..e5798f70d149 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/exynos4-is/fimc-lite.c
@@ -90,7 +90,7 @@ static const struct fimc_fmt fimc_lite_formats[] = {
.name = "RAW10 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG10,
.colorspace = V4L2_COLORSPACE_SRGB,
- .depth = { 10 },
+ .depth = { 16 },
.color = FIMC_FMT_RAW10,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10,
@@ -99,7 +99,7 @@ static const struct fimc_fmt fimc_lite_formats[] = {
.name = "RAW12 (GRBG)",
.fourcc = V4L2_PIX_FMT_SGRBG12,
.colorspace = V4L2_COLORSPACE_SRGB,
- .depth = { 12 },
+ .depth = { 16 },
.color = FIMC_FMT_RAW12,
.memplanes = 1,
.mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12,
@@ -1504,16 +1504,17 @@ static int fimc_lite_probe(struct platform_device *pdev)
struct resource *res;
int ret;
+ if (!dev->of_node)
+ return -ENODEV;
+
fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
return -ENOMEM;
- if (dev->of_node) {
- of_id = of_match_node(flite_of_match, dev->of_node);
- if (of_id)
- drv_data = (struct flite_drvdata *)of_id->data;
- fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
- }
+ of_id = of_match_node(flite_of_match, dev->of_node);
+ if (of_id)
+ drv_data = (struct flite_drvdata *)of_id->data;
+ fimc->index = of_alias_get_id(dev->of_node, "fimc-lite");
if (!drv_data || fimc->index >= drv_data->num_instances ||
fimc->index < 0) {
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c
index f8c66b434fd6..a83511278317 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/exynos4-is/media-dev.c
@@ -1149,7 +1149,6 @@ static void fimc_md_put_clocks(struct fimc_md *fmd)
while (--i >= 0) {
if (IS_ERR(fmd->camclk[i].clock))
continue;
- clk_unprepare(fmd->camclk[i].clock);
clk_put(fmd->camclk[i].clock);
fmd->camclk[i].clock = ERR_PTR(-EINVAL);
}
@@ -1168,7 +1167,7 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
struct device *dev = NULL;
char clk_name[32];
struct clk *clock;
- int ret, i;
+ int i, ret = 0;
for (i = 0; i < FIMC_MAX_CAMCLKS; i++)
fmd->camclk[i].clock = ERR_PTR(-EINVAL);
@@ -1186,12 +1185,6 @@ static int fimc_md_get_clocks(struct fimc_md *fmd)
ret = PTR_ERR(clock);
break;
}
- ret = clk_prepare(clock);
- if (ret < 0) {
- clk_put(clock);
- fmd->camclk[i].clock = ERR_PTR(-EINVAL);
- break;
- }
fmd->camclk[i].clock = clock;
}
if (ret)
@@ -1248,7 +1241,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
ret = pm_runtime_get_sync(fmd->pmf);
if (ret < 0)
return ret;
- ret = clk_enable(camclk->clock);
+ ret = clk_prepare_enable(camclk->clock);
dbg("Enabled camclk %d: f: %lu", si->clk_id,
clk_get_rate(camclk->clock));
}
@@ -1259,7 +1252,7 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd,
return 0;
if (--camclk->use_count == 0) {
- clk_disable(camclk->clock);
+ clk_disable_unprepare(camclk->clock);
pm_runtime_put(fmd->pmf);
dbg("Disabled camclk %d", si->clk_id);
}
@@ -1529,9 +1522,9 @@ static int fimc_md_probe(struct platform_device *pdev)
err_unlock:
mutex_unlock(&fmd->media_dev.graph_mutex);
err_clk:
- media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
fimc_md_unregister_entities(fmd);
+ media_device_unregister(&fmd->media_dev);
err_md:
v4l2_device_unregister(&fmd->v4l2_dev);
return ret;
@@ -1543,6 +1536,8 @@ static int fimc_md_remove(struct platform_device *pdev)
if (!fmd)
return 0;
+
+ v4l2_device_unregister(&fmd->v4l2_dev);
device_remove_file(&pdev->dev, &dev_attr_subdev_conf_mode);
fimc_md_unregister_entities(fmd);
fimc_md_pipelines_free(fmd);
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell-ccic/cafe-driver.c
index 1f079ff33d4b..562845361246 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell-ccic/cafe-driver.c
@@ -399,7 +399,7 @@ static void cafe_ctlr_init(struct mcam_camera *mcam)
}
-static void cafe_ctlr_power_up(struct mcam_camera *mcam)
+static int cafe_ctlr_power_up(struct mcam_camera *mcam)
{
/*
* Part one of the sensor dance: turn the global
@@ -414,6 +414,8 @@ static void cafe_ctlr_power_up(struct mcam_camera *mcam)
*/
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
mcam_reg_write(mcam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+
+ return 0;
}
static void cafe_ctlr_power_down(struct mcam_camera *mcam)
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c
index 0821ed08c122..5184887b155c 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell-ccic/mcam-core.c
@@ -19,6 +19,7 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
@@ -93,6 +94,9 @@ MODULE_PARM_DESC(buffer_mode,
#define CF_CONFIG_NEEDED 4 /* Must configure hardware */
#define CF_SINGLE_BUFFER 5 /* Running with a single buffer */
#define CF_SG_RESTART 6 /* SG restart needed */
+#define CF_FRAME_SOF0 7 /* Frame 0 started */
+#define CF_FRAME_SOF1 8
+#define CF_FRAME_SOF2 9
#define sensor_call(cam, o, f, args...) \
v4l2_subdev_call(cam->sensor, o, f, ##args)
@@ -101,6 +105,7 @@ static struct mcam_format_struct {
__u8 *desc;
__u32 pixelformat;
int bpp; /* Bytes per pixel */
+ bool planar;
enum v4l2_mbus_pixelcode mbus_code;
} mcam_formats[] = {
{
@@ -108,24 +113,56 @@ static struct mcam_format_struct {
.pixelformat = V4L2_PIX_FMT_YUYV,
.mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
.bpp = 2,
+ .planar = false,
+ },
+ {
+ .desc = "UYVY 4:2:2",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = false,
+ },
+ {
+ .desc = "YUV 4:2:2 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YUV422P,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
+ },
+ {
+ .desc = "YUV 4:2:0 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
+ },
+ {
+ .desc = "YVU 4:2:0 PLANAR",
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 2,
+ .planar = true,
},
{
.desc = "RGB 444",
.pixelformat = V4L2_PIX_FMT_RGB444,
.mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
.bpp = 2,
+ .planar = false,
},
{
.desc = "RGB 565",
.pixelformat = V4L2_PIX_FMT_RGB565,
.mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
.bpp = 2,
+ .planar = false,
},
{
.desc = "Raw RGB Bayer",
.pixelformat = V4L2_PIX_FMT_SBGGR8,
.mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,
- .bpp = 1
+ .bpp = 1,
+ .planar = false,
},
};
#define N_MCAM_FMTS ARRAY_SIZE(mcam_formats)
@@ -168,6 +205,12 @@ struct mcam_dma_desc {
u32 segment_len;
};
+struct yuv_pointer_t {
+ dma_addr_t y;
+ dma_addr_t u;
+ dma_addr_t v;
+};
+
/*
* Our buffer type for working with videobuf2. Note that the vb2
* developers have decreed that struct vb2_buffer must be at the
@@ -179,6 +222,7 @@ struct mcam_vb_buffer {
struct mcam_dma_desc *dma_desc; /* Descriptor virtual address */
dma_addr_t dma_desc_pa; /* Descriptor physical address */
int dma_desc_nent; /* Number of mapped descriptors */
+ struct yuv_pointer_t yuv_p;
};
static inline struct mcam_vb_buffer *vb_to_mvb(struct vb2_buffer *vb)
@@ -219,8 +263,10 @@ static void mcam_reset_buffers(struct mcam_camera *cam)
int i;
cam->next_buf = -1;
- for (i = 0; i < cam->nbufs; i++)
+ for (i = 0; i < cam->nbufs; i++) {
clear_bit(i, &cam->flags);
+ clear_bit(CF_FRAME_SOF0 + i, &cam->flags);
+ }
}
static inline int mcam_needs_config(struct mcam_camera *cam)
@@ -253,6 +299,45 @@ static void mcam_ctlr_stop(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL0, C0_ENABLE);
}
+static void mcam_enable_mipi(struct mcam_camera *mcam)
+{
+ /* Using MIPI mode and enable MIPI */
+ cam_dbg(mcam, "camera: DPHY3=0x%x, DPHY5=0x%x, DPHY6=0x%x\n",
+ mcam->dphy[0], mcam->dphy[1], mcam->dphy[2]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY3, mcam->dphy[0]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY5, mcam->dphy[1]);
+ mcam_reg_write(mcam, REG_CSI2_DPHY6, mcam->dphy[2]);
+
+ if (!mcam->mipi_enabled) {
+ if (mcam->lane > 4 || mcam->lane <= 0) {
+ cam_warn(mcam, "lane number error\n");
+ mcam->lane = 1; /* set the default value */
+ }
+ /*
+ * 0x41 actives 1 lane
+ * 0x43 actives 2 lanes
+ * 0x45 actives 3 lanes (never happen)
+ * 0x47 actives 4 lanes
+ */
+ mcam_reg_write(mcam, REG_CSI2_CTRL0,
+ CSI2_C0_MIPI_EN | CSI2_C0_ACT_LANE(mcam->lane));
+ mcam_reg_write(mcam, REG_CLKCTRL,
+ (mcam->mclk_src << 29) | mcam->mclk_div);
+
+ mcam->mipi_enabled = true;
+ }
+}
+
+static void mcam_disable_mipi(struct mcam_camera *mcam)
+{
+ /* Using Parallel mode or disable MIPI */
+ mcam_reg_write(mcam, REG_CSI2_CTRL0, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY3, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY5, 0x0);
+ mcam_reg_write(mcam, REG_CSI2_DPHY6, 0x0);
+ mcam->mipi_enabled = false;
+}
+
/* ------------------------------------------------------------------- */
#ifdef MCAM_MODE_VMALLOC
@@ -425,6 +510,15 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
/*
* DMA-contiguous code.
*/
+
+static bool mcam_fmt_is_planar(__u32 pfmt)
+{
+ struct mcam_format_struct *f;
+
+ f = mcam_find_format(pfmt);
+ return f->planar;
+}
+
/*
* Set up a contiguous buffer for the given frame. Here also is where
* the underrun strategy is set: if there is no buffer available, reuse
@@ -436,27 +530,58 @@ static inline int mcam_check_dma_buffers(struct mcam_camera *cam)
static void mcam_set_contig_buffer(struct mcam_camera *cam, int frame)
{
struct mcam_vb_buffer *buf;
+ struct v4l2_pix_format *fmt = &cam->pix_format;
+ dma_addr_t dma_handle;
+ u32 pixel_count = fmt->width * fmt->height;
+ struct vb2_buffer *vb;
+
/*
* If there are no available buffers, go into single mode
*/
if (list_empty(&cam->buffers)) {
buf = cam->vb_bufs[frame ^ 0x1];
- cam->vb_bufs[frame] = buf;
- mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
set_bit(CF_SINGLE_BUFFER, &cam->flags);
cam->frame_state.singles++;
- return;
+ } else {
+ /*
+ * OK, we have a buffer we can use.
+ */
+ buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer,
+ queue);
+ list_del_init(&buf->queue);
+ clear_bit(CF_SINGLE_BUFFER, &cam->flags);
}
- /*
- * OK, we have a buffer we can use.
- */
- buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
- list_del_init(&buf->queue);
- mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR,
- vb2_dma_contig_plane_dma_addr(&buf->vb_buf, 0));
+
cam->vb_bufs[frame] = buf;
- clear_bit(CF_SINGLE_BUFFER, &cam->flags);
+ vb = &buf->vb_buf;
+
+ dma_handle = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf->yuv_p.y = dma_handle;
+
+ switch (cam->pix_format.pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ buf->yuv_p.u = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.v = buf->yuv_p.u + pixel_count / 2;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ buf->yuv_p.u = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.v = buf->yuv_p.u + pixel_count / 4;
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ buf->yuv_p.v = buf->yuv_p.y + pixel_count;
+ buf->yuv_p.u = buf->yuv_p.v + pixel_count / 4;
+ break;
+ default:
+ break;
+ }
+
+ mcam_reg_write(cam, frame == 0 ? REG_Y0BAR : REG_Y1BAR, buf->yuv_p.y);
+ if (mcam_fmt_is_planar(fmt->pixelformat)) {
+ mcam_reg_write(cam, frame == 0 ?
+ REG_U0BAR : REG_U1BAR, buf->yuv_p.u);
+ mcam_reg_write(cam, frame == 0 ?
+ REG_V0BAR : REG_V1BAR, buf->yuv_p.v);
+ }
}
/*
@@ -614,48 +739,90 @@ static inline void mcam_sg_restart(struct mcam_camera *cam)
*/
static void mcam_ctlr_image(struct mcam_camera *cam)
{
- int imgsz;
struct v4l2_pix_format *fmt = &cam->pix_format;
+ u32 widthy = 0, widthuv = 0, imgsz_h, imgsz_w;
+
+ cam_dbg(cam, "camera: bytesperline = %d; height = %d\n",
+ fmt->bytesperline, fmt->sizeimage / fmt->bytesperline);
+ imgsz_h = (fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK;
+ imgsz_w = (fmt->width * 2) & IMGSZ_H_MASK;
+
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_UYVY:
+ widthy = fmt->width * 2;
+ widthuv = 0;
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ imgsz_h = (fmt->sizeimage / fmt->bytesperline) << IMGSZ_V_SHIFT;
+ widthy = fmt->bytesperline;
+ widthuv = 0;
+ break;
+ case V4L2_PIX_FMT_YUV422P:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ widthy = fmt->width;
+ widthuv = fmt->width / 2;
+ break;
+ default:
+ widthy = fmt->bytesperline;
+ widthuv = 0;
+ }
+
+ mcam_reg_write_mask(cam, REG_IMGPITCH, widthuv << 16 | widthy,
+ IMGP_YP_MASK | IMGP_UVP_MASK);
+ mcam_reg_write(cam, REG_IMGSIZE, imgsz_h | imgsz_w);
+ mcam_reg_write(cam, REG_IMGOFFSET, 0x0);
- imgsz = ((fmt->height << IMGSZ_V_SHIFT) & IMGSZ_V_MASK) |
- (fmt->bytesperline & IMGSZ_H_MASK);
- mcam_reg_write(cam, REG_IMGSIZE, imgsz);
- mcam_reg_write(cam, REG_IMGOFFSET, 0);
- /* YPITCH just drops the last two bits */
- mcam_reg_write_mask(cam, REG_IMGPITCH, fmt->bytesperline,
- IMGP_YP_MASK);
/*
* Tell the controller about the image format we are using.
*/
- switch (cam->pix_format.pixelformat) {
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_YUV422P:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PLANAR | C0_YUVE_YVYU, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_420PL | C0_YUVE_YVYU, C0_DF_MASK);
+ break;
case V4L2_PIX_FMT_YUYV:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_YUV|C0_YUV_PACKED|C0_YUVE_YUYV,
- C0_DF_MASK);
- break;
-
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_UYVY, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
+ break;
+ case V4L2_PIX_FMT_JPEG:
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_YUV | C0_YUV_PACKED | C0_YUVE_YUYV, C0_DF_MASK);
+ break;
case V4L2_PIX_FMT_RGB444:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_444|C0_RGB4_XRGB,
- C0_DF_MASK);
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB | C0_RGBF_444 | C0_RGB4_XRGB, C0_DF_MASK);
/* Alpha value? */
- break;
-
+ break;
case V4L2_PIX_FMT_RGB565:
- mcam_reg_write_mask(cam, REG_CTRL0,
- C0_DF_RGB|C0_RGBF_565|C0_RGB5_BGGR,
- C0_DF_MASK);
- break;
-
+ mcam_reg_write_mask(cam, REG_CTRL0,
+ C0_DF_RGB | C0_RGBF_565 | C0_RGB5_BGGR, C0_DF_MASK);
+ break;
default:
- cam_err(cam, "Unknown format %x\n", cam->pix_format.pixelformat);
- break;
+ cam_err(cam, "camera: unknown format: %#x\n", fmt->pixelformat);
+ break;
}
+
/*
* Make sure it knows we want to use hsync/vsync.
*/
- mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC,
- C0_SIFM_MASK);
+ mcam_reg_write_mask(cam, REG_CTRL0, C0_SIF_HVSYNC, C0_SIFM_MASK);
+ /*
+ * This field controls the generation of EOF(DVP only)
+ */
+ if (cam->bus_type != V4L2_MBUS_CSI2)
+ mcam_reg_set_bit(cam, REG_CTRL0,
+ C0_EOF_VSYNC | C0_VEDGE_CTRL);
}
@@ -753,15 +920,21 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
/*
* Power up and down.
*/
-static void mcam_ctlr_power_up(struct mcam_camera *cam)
+static int mcam_ctlr_power_up(struct mcam_camera *cam)
{
unsigned long flags;
+ int ret;
spin_lock_irqsave(&cam->dev_lock, flags);
- cam->plat_power_up(cam);
+ ret = cam->plat_power_up(cam);
+ if (ret) {
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ return ret;
+ }
mcam_reg_clear_bit(cam, REG_CTRL1, C1_PWRDWN);
spin_unlock_irqrestore(&cam->dev_lock, flags);
msleep(5); /* Just to be sure */
+ return 0;
}
static void mcam_ctlr_power_down(struct mcam_camera *cam)
@@ -869,6 +1042,17 @@ static int mcam_read_setup(struct mcam_camera *cam)
spin_lock_irqsave(&cam->dev_lock, flags);
clear_bit(CF_DMA_ACTIVE, &cam->flags);
mcam_reset_buffers(cam);
+ /*
+ * Update CSI2_DPHY value
+ */
+ if (cam->calc_dphy)
+ cam->calc_dphy(cam);
+ cam_dbg(cam, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
+ cam->dphy[0], cam->dphy[1], cam->dphy[2]);
+ if (cam->bus_type == V4L2_MBUS_CSI2)
+ mcam_enable_mipi(cam);
+ else
+ mcam_disable_mipi(cam);
mcam_ctlr_irq_enable(cam);
cam->state = S_STREAMING;
if (!test_bit(CF_SG_RESTART, &cam->flags))
@@ -943,6 +1127,7 @@ static void mcam_vb_wait_finish(struct vb2_queue *vq)
static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct mcam_camera *cam = vb2_get_drv_priv(vq);
+ unsigned int frame;
if (cam->state != S_IDLE) {
INIT_LIST_HEAD(&cam->buffers);
@@ -960,6 +1145,14 @@ static int mcam_vb_start_streaming(struct vb2_queue *vq, unsigned int count)
cam->state = S_BUFWAIT;
return 0;
}
+
+ /*
+ * Ensure clear the left over frame flags
+ * before every really start streaming
+ */
+ for (frame = 0; frame < cam->nbufs; frame++)
+ clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
+
return mcam_read_setup(cam);
}
@@ -977,6 +1170,12 @@ static int mcam_vb_stop_streaming(struct vb2_queue *vq)
return -EINVAL;
mcam_ctlr_stop_dma(cam);
/*
+ * Reset the CCIC PHY after stopping streaming,
+ * otherwise, the CCIC may be unstable.
+ */
+ if (cam->ctlr_reset)
+ cam->ctlr_reset(cam);
+ /*
* VB2 reclaims the buffers, so we need to forget
* about them.
*/
@@ -1087,6 +1286,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_CONTIG
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_dma_contig_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
cam->vb_alloc_ctx = vb2_dma_contig_init_ctx(cam->dev);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_contig;
@@ -1097,6 +1297,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
#ifdef MCAM_MODE_DMA_SG
vq->ops = &mcam_vb2_sg_ops;
vq->mem_ops = &vb2_dma_sg_memops;
+ vq->buf_struct_size = sizeof(struct mcam_vb_buffer);
vq->io_modes = VB2_MMAP | VB2_USERPTR;
cam->dma_setup = mcam_ctlr_dma_sg;
cam->frame_complete = mcam_dma_sg_done;
@@ -1247,7 +1448,15 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
ret = sensor_call(cam, video, try_mbus_fmt, &mbus_fmt);
mutex_unlock(&cam->s_mutex);
v4l2_fill_pix_format(pix, &mbus_fmt);
- pix->bytesperline = pix->width * f->bpp;
+ switch (f->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ pix->bytesperline = pix->width * 3 / 2;
+ break;
+ default:
+ pix->bytesperline = pix->width * f->bpp;
+ break;
+ }
pix->sizeimage = pix->height * pix->bytesperline;
return ret;
}
@@ -1475,7 +1684,9 @@ static int mcam_v4l_open(struct file *filp)
ret = mcam_setup_vb2(cam);
if (ret)
goto out;
- mcam_ctlr_power_up(cam);
+ ret = mcam_ctlr_power_up(cam);
+ if (ret)
+ goto out;
__mcam_cam_reset(cam);
mcam_set_config_needed(cam, 1);
}
@@ -1498,10 +1709,12 @@ static int mcam_v4l_release(struct file *filp)
if (cam->users == 0) {
mcam_ctlr_stop_dma(cam);
mcam_cleanup_vb2(cam);
+ mcam_disable_mipi(cam);
mcam_ctlr_power_down(cam);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
mcam_free_dma_bufs(cam);
}
+
mutex_unlock(&cam->s_mutex);
return 0;
}
@@ -1617,9 +1830,11 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* each time.
*/
for (frame = 0; frame < cam->nbufs; frame++)
- if (irqs & (IRQ_EOF0 << frame)) {
+ if (irqs & (IRQ_EOF0 << frame) &&
+ test_bit(CF_FRAME_SOF0 + frame, &cam->flags)) {
mcam_frame_complete(cam, frame);
handled = 1;
+ clear_bit(CF_FRAME_SOF0 + frame, &cam->flags);
if (cam->buffer_mode == B_DMA_sg)
break;
}
@@ -1628,9 +1843,15 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
* code assumes that we won't get multiple frame interrupts
* at once; may want to rethink that.
*/
- if (irqs & (IRQ_SOF0 | IRQ_SOF1 | IRQ_SOF2)) {
+ for (frame = 0; frame < cam->nbufs; frame++) {
+ if (irqs & (IRQ_SOF0 << frame)) {
+ set_bit(CF_FRAME_SOF0 + frame, &cam->flags);
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ if (handled == IRQ_HANDLED) {
set_bit(CF_DMA_ACTIVE, &cam->flags);
- handled = 1;
if (cam->buffer_mode == B_DMA_sg)
mcam_ctlr_stop(cam);
}
@@ -1787,7 +2008,11 @@ int mccic_resume(struct mcam_camera *cam)
mutex_lock(&cam->s_mutex);
if (cam->users > 0) {
- mcam_ctlr_power_up(cam);
+ ret = mcam_ctlr_power_up(cam);
+ if (ret) {
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
__mcam_cam_reset(cam);
} else {
mcam_ctlr_power_down(cam);
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell-ccic/mcam-core.h
index 520c8ded9443..e0e628cb98f9 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell-ccic/mcam-core.h
@@ -88,6 +88,8 @@ struct mcam_frame_state {
unsigned int delivered;
};
+#define NR_MCAM_CLK 3
+
/*
* A description of one of our devices.
* Locking: controlled by s_mutex. Certain fields, however, require
@@ -108,11 +110,33 @@ struct mcam_camera {
short int clock_speed; /* Sensor clock speed, default 30 */
short int use_smbus; /* SMBUS or straight I2c? */
enum mcam_buffer_mode buffer_mode;
+
+ int mclk_min; /* The minimal value of mclk */
+ int mclk_src; /* which clock source the mclk derives from */
+ int mclk_div; /* Clock Divider Value for MCLK */
+
+ int ccic_id;
+ enum v4l2_mbus_type bus_type;
+ /* MIPI support */
+ /* The dphy config value, allocated in board file
+ * dphy[0]: DPHY3
+ * dphy[1]: DPHY5
+ * dphy[2]: DPHY6
+ */
+ int *dphy;
+ bool mipi_enabled; /* flag whether mipi is enabled already */
+ int lane; /* lane number */
+
+ /* clock tree support */
+ struct clk *clk[NR_MCAM_CLK];
+
/*
* Callbacks from the core to the platform code.
*/
- void (*plat_power_up) (struct mcam_camera *cam);
+ int (*plat_power_up) (struct mcam_camera *cam);
void (*plat_power_down) (struct mcam_camera *cam);
+ void (*calc_dphy) (struct mcam_camera *cam);
+ void (*ctlr_reset) (struct mcam_camera *cam);
/*
* Everything below here is private to the mcam core and
@@ -225,6 +249,23 @@ int mccic_resume(struct mcam_camera *cam);
#define REG_Y0BAR 0x00
#define REG_Y1BAR 0x04
#define REG_Y2BAR 0x08
+#define REG_U0BAR 0x0c
+#define REG_U1BAR 0x10
+#define REG_U2BAR 0x14
+#define REG_V0BAR 0x18
+#define REG_V1BAR 0x1C
+#define REG_V2BAR 0x20
+
+/*
+ * register definitions for MIPI support
+ */
+#define REG_CSI2_CTRL0 0x100
+#define CSI2_C0_MIPI_EN (0x1 << 0)
+#define CSI2_C0_ACT_LANE(n) ((n-1) << 1)
+#define REG_CSI2_DPHY3 0x12c
+#define REG_CSI2_DPHY5 0x134
+#define REG_CSI2_DPHY6 0x138
+
/* ... */
#define REG_IMGPITCH 0x24 /* Image pitch register */
@@ -293,13 +334,16 @@ int mccic_resume(struct mcam_camera *cam);
#define C0_YUVE_XUVY 0x00020000 /* 420: .UVY */
#define C0_YUVE_XVUY 0x00030000 /* 420: .VUY */
/* Bayer bits 18,19 if needed */
+#define C0_EOF_VSYNC 0x00400000 /* Generate EOF by VSYNC */
+#define C0_VEDGE_CTRL 0x00800000 /* Detect falling edge of VSYNC */
#define C0_HPOL_LOW 0x01000000 /* HSYNC polarity active low */
#define C0_VPOL_LOW 0x02000000 /* VSYNC polarity active low */
#define C0_VCLK_LOW 0x04000000 /* VCLK on falling edge */
#define C0_DOWNSCALE 0x08000000 /* Enable downscaler */
-#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
+/* SIFMODE */
#define C0_SIF_HVSYNC 0x00000000 /* Use H/VSYNC */
-#define CO_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+#define C0_SOF_NOSYNC 0x40000000 /* Use inband active signaling */
+#define C0_SIFM_MASK 0xc0000000 /* SIF mode bits */
/* Bits below C1_444ALPHA are not present in Cafe */
#define REG_CTRL1 0x40 /* Control 1 */
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell-ccic/mmp-driver.c
index a634888271cd..b5a19af5c587 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell-ccic/mmp-driver.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/pm.h>
+#include <linux/clk.h>
#include "mcam-core.h"
@@ -33,11 +34,14 @@ MODULE_ALIAS("platform:mmp-camera");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
MODULE_LICENSE("GPL");
+static char *mcam_clks[] = {"CCICAXICLK", "CCICFUNCLK", "CCICPHYCLK"};
+
struct mmp_camera {
void *power_regs;
struct platform_device *pdev;
struct mcam_camera mcam;
struct list_head devlist;
+ struct clk *mipi_clk;
int irq;
};
@@ -101,6 +105,27 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
#define CPU_SUBSYS_PMU_BASE 0xd4282800
#define REG_CCIC_DCGCR 0x28 /* CCIC dyn clock gate ctrl reg */
#define REG_CCIC_CRCR 0x50 /* CCIC clk reset ctrl reg */
+#define REG_CCIC2_CRCR 0xf4 /* CCIC2 clk reset ctrl reg */
+
+static void mcam_clk_enable(struct mcam_camera *mcam)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_prepare_enable(mcam->clk[i]);
+ }
+}
+
+static void mcam_clk_disable(struct mcam_camera *mcam)
+{
+ int i;
+
+ for (i = NR_MCAM_CLK - 1; i >= 0; i--) {
+ if (!IS_ERR(mcam->clk[i]))
+ clk_disable_unprepare(mcam->clk[i]);
+ }
+}
/*
* Power control.
@@ -112,10 +137,17 @@ static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
mdelay(1);
}
-static void mmpcam_power_up(struct mcam_camera *mcam)
+static int mmpcam_power_up(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
struct mmp_camera_platform_data *pdata;
+
+ if (mcam->bus_type == V4L2_MBUS_CSI2) {
+ cam->mipi_clk = devm_clk_get(mcam->dev, "mipi");
+ if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0))
+ return PTR_ERR(cam->mipi_clk);
+ }
+
/*
* Turn on power and clocks to the controller.
*/
@@ -132,6 +164,10 @@ static void mmpcam_power_up(struct mcam_camera *mcam)
mdelay(5);
gpio_set_value(pdata->sensor_reset_gpio, 1); /* reset is active low */
mdelay(5);
+
+ mcam_clk_enable(mcam);
+
+ return 0;
}
static void mmpcam_power_down(struct mcam_camera *mcam)
@@ -149,8 +185,133 @@ static void mmpcam_power_down(struct mcam_camera *mcam)
pdata = cam->pdev->dev.platform_data;
gpio_set_value(pdata->sensor_power_gpio, 0);
gpio_set_value(pdata->sensor_reset_gpio, 0);
+
+ if (mcam->bus_type == V4L2_MBUS_CSI2 && !IS_ERR(cam->mipi_clk)) {
+ if (cam->mipi_clk)
+ devm_clk_put(mcam->dev, cam->mipi_clk);
+ cam->mipi_clk = NULL;
+ }
+
+ mcam_clk_disable(mcam);
}
+void mcam_ctlr_reset(struct mcam_camera *mcam)
+{
+ unsigned long val;
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+
+ if (mcam->ccic_id) {
+ /*
+ * Using CCIC2
+ */
+ val = ioread32(cam->power_regs + REG_CCIC2_CRCR);
+ iowrite32(val & ~0x2, cam->power_regs + REG_CCIC2_CRCR);
+ iowrite32(val | 0x2, cam->power_regs + REG_CCIC2_CRCR);
+ } else {
+ /*
+ * Using CCIC1
+ */
+ val = ioread32(cam->power_regs + REG_CCIC_CRCR);
+ iowrite32(val & ~0x2, cam->power_regs + REG_CCIC_CRCR);
+ iowrite32(val | 0x2, cam->power_regs + REG_CCIC_CRCR);
+ }
+}
+
+/*
+ * calc the dphy register values
+ * There are three dphy registers being used.
+ * dphy[0] - CSI2_DPHY3
+ * dphy[1] - CSI2_DPHY5
+ * dphy[2] - CSI2_DPHY6
+ * CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
+ * or be calculated dynamically
+ */
+void mmpcam_calc_dphy(struct mcam_camera *mcam)
+{
+ struct mmp_camera *cam = mcam_to_cam(mcam);
+ struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
+ struct device *dev = &cam->pdev->dev;
+ unsigned long tx_clk_esc;
+
+ /*
+ * If CSI2_DPHY3 is calculated dynamically,
+ * pdata->lane_clk should be already set
+ * either in the board driver statically
+ * or in the sensor driver dynamically.
+ */
+ /*
+ * dphy[0] - CSI2_DPHY3:
+ * bit 0 ~ bit 7: HS Term Enable.
+ * defines the time that the DPHY
+ * wait before enabling the data
+ * lane termination after detecting
+ * that the sensor has driven the data
+ * lanes to the LP00 bridge state.
+ * The value is calculated by:
+ * (Max T(D_TERM_EN)/Period(DDR)) - 1
+ * bit 8 ~ bit 15: HS_SETTLE
+ * Time interval during which the HS
+ * receiver shall ignore any Data Lane
+ * HS transistions.
+ * The vaule has been calibrated on
+ * different boards. It seems to work well.
+ *
+ * More detail please refer
+ * MIPI Alliance Spectification for D-PHY
+ * document for explanation of HS-SETTLE
+ * and D-TERM-EN.
+ */
+ switch (pdata->dphy3_algo) {
+ case DPHY3_ALGO_PXA910:
+ /*
+ * Calculate CSI2_DPHY3 algo for PXA910
+ */
+ pdata->dphy[0] =
+ (((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8)
+ | (1 + pdata->lane_clk * 35 / 1000);
+ break;
+ case DPHY3_ALGO_PXA2128:
+ /*
+ * Calculate CSI2_DPHY3 algo for PXA2128
+ */
+ pdata->dphy[0] =
+ (((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8)
+ | (1 + pdata->lane_clk * 35 / 1000);
+ break;
+ default:
+ /*
+ * Use default CSI2_DPHY3 value for PXA688/PXA988
+ */
+ dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n");
+ }
+
+ /*
+ * mipi_clk will never be changed, it is a fixed value on MMP
+ */
+ if (IS_ERR(cam->mipi_clk))
+ return;
+
+ /* get the escape clk, this is hard coded */
+ tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12;
+
+ /*
+ * dphy[2] - CSI2_DPHY6:
+ * bit 0 ~ bit 7: CK Term Enable
+ * Time for the Clock Lane receiver to enable the HS line
+ * termination. The value is calculated similarly with
+ * HS Term Enable
+ * bit 8 ~ bit 15: CK Settle
+ * Time interval during which the HS receiver shall ignore
+ * any Clock Lane HS transitions.
+ * The value is calibrated on the boards.
+ */
+ pdata->dphy[2] =
+ ((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8)
+ | (((38 * tx_clk_esc) / 1000 - 1) & 0xff);
+
+ dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
+ pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]);
+}
static irqreturn_t mmpcam_irq(int irq, void *data)
{
@@ -164,6 +325,35 @@ static irqreturn_t mmpcam_irq(int irq, void *data)
return IRQ_RETVAL(handled);
}
+static void mcam_deinit_clk(struct mcam_camera *mcam)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (!IS_ERR(mcam->clk[i])) {
+ if (mcam->clk[i])
+ devm_clk_put(mcam->dev, mcam->clk[i]);
+ }
+ mcam->clk[i] = NULL;
+ }
+}
+
+static void mcam_init_clk(struct mcam_camera *mcam)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_MCAM_CLK; i++) {
+ if (mcam_clks[i] != NULL) {
+ /* Some clks are not necessary on some boards
+ * We still try to run even it fails getting clk
+ */
+ mcam->clk[i] = devm_clk_get(mcam->dev, mcam_clks[i]);
+ if (IS_ERR(mcam->clk[i]))
+ dev_warn(mcam->dev, "Could not get clk: %s\n",
+ mcam_clks[i]);
+ }
+ }
+}
static int mmpcam_probe(struct platform_device *pdev)
{
@@ -173,17 +363,32 @@ static int mmpcam_probe(struct platform_device *pdev)
struct mmp_camera_platform_data *pdata;
int ret;
- cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ pdata = pdev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
if (cam == NULL)
return -ENOMEM;
cam->pdev = pdev;
+ cam->mipi_clk = NULL;
INIT_LIST_HEAD(&cam->devlist);
mcam = &cam->mcam;
mcam->plat_power_up = mmpcam_power_up;
mcam->plat_power_down = mmpcam_power_down;
+ mcam->ctlr_reset = mcam_ctlr_reset;
+ mcam->calc_dphy = mmpcam_calc_dphy;
mcam->dev = &pdev->dev;
mcam->use_smbus = 0;
+ mcam->ccic_id = pdev->id;
+ mcam->mclk_min = pdata->mclk_min;
+ mcam->mclk_src = pdata->mclk_src;
+ mcam->mclk_div = pdata->mclk_div;
+ mcam->bus_type = pdata->bus_type;
+ mcam->dphy = pdata->dphy;
+ mcam->mipi_enabled = false;
+ mcam->lane = pdata->lane;
mcam->chip_id = MCAM_ARMADA610;
mcam->buffer_mode = B_DMA_sg;
spin_lock_init(&mcam->dev_lock);
@@ -191,69 +396,58 @@ static int mmpcam_probe(struct platform_device *pdev)
* Get our I/O memory.
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "no iomem resource!\n");
- ret = -ENODEV;
- goto out_free;
- }
- mcam->regs = ioremap(res->start, resource_size(res));
- if (mcam->regs == NULL) {
- dev_err(&pdev->dev, "MMIO ioremap fail\n");
- ret = -ENODEV;
- goto out_free;
- }
+ mcam->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(mcam->regs))
+ return PTR_ERR(mcam->regs);
mcam->regs_size = resource_size(res);
/*
* Power/clock memory is elsewhere; get it too. Perhaps this
* should really be managed outside of this driver?
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (res == NULL) {
- dev_err(&pdev->dev, "no power resource!\n");
- ret = -ENODEV;
- goto out_unmap1;
- }
- cam->power_regs = ioremap(res->start, resource_size(res));
- if (cam->power_regs == NULL) {
- dev_err(&pdev->dev, "power MMIO ioremap fail\n");
- ret = -ENODEV;
- goto out_unmap1;
- }
+ cam->power_regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(cam->power_regs))
+ return PTR_ERR(cam->power_regs);
/*
* Find the i2c adapter. This assumes, of course, that the
* i2c bus is already up and functioning.
*/
- pdata = pdev->dev.platform_data;
mcam->i2c_adapter = platform_get_drvdata(pdata->i2c_device);
if (mcam->i2c_adapter == NULL) {
- ret = -ENODEV;
dev_err(&pdev->dev, "No i2c adapter\n");
- goto out_unmap2;
+ return -ENODEV;
}
/*
* Sensor GPIO pins.
*/
- ret = gpio_request(pdata->sensor_power_gpio, "cam-power");
+ ret = devm_gpio_request(&pdev->dev, pdata->sensor_power_gpio,
+ "cam-power");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor power gpio %d",
pdata->sensor_power_gpio);
- goto out_unmap2;
+ return ret;
}
gpio_direction_output(pdata->sensor_power_gpio, 0);
- ret = gpio_request(pdata->sensor_reset_gpio, "cam-reset");
+ ret = devm_gpio_request(&pdev->dev, pdata->sensor_reset_gpio,
+ "cam-reset");
if (ret) {
dev_err(&pdev->dev, "Can't get sensor reset gpio %d",
pdata->sensor_reset_gpio);
- goto out_gpio;
+ return ret;
}
gpio_direction_output(pdata->sensor_reset_gpio, 0);
+
+ mcam_init_clk(mcam);
+
/*
* Power the device up and hand it off to the core.
*/
- mmpcam_power_up(mcam);
+ ret = mmpcam_power_up(mcam);
+ if (ret)
+ goto out_deinit_clk;
ret = mccic_register(mcam);
if (ret)
- goto out_gpio2;
+ goto out_power_down;
/*
* Finally, set up our IRQ now that the core is ready to
* deal with it.
@@ -264,8 +458,8 @@ static int mmpcam_probe(struct platform_device *pdev)
goto out_unregister;
}
cam->irq = res->start;
- ret = request_irq(cam->irq, mmpcam_irq, IRQF_SHARED,
- "mmp-camera", mcam);
+ ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
+ "mmp-camera", mcam);
if (ret == 0) {
mmpcam_add_device(cam);
return 0;
@@ -273,17 +467,10 @@ static int mmpcam_probe(struct platform_device *pdev)
out_unregister:
mccic_shutdown(mcam);
-out_gpio2:
+out_power_down:
mmpcam_power_down(mcam);
- gpio_free(pdata->sensor_reset_gpio);
-out_gpio:
- gpio_free(pdata->sensor_power_gpio);
-out_unmap2:
- iounmap(cam->power_regs);
-out_unmap1:
- iounmap(mcam->regs);
-out_free:
- kfree(cam);
+out_deinit_clk:
+ mcam_deinit_clk(mcam);
return ret;
}
@@ -300,6 +487,7 @@ static int mmpcam_remove(struct mmp_camera *cam)
pdata = cam->pdev->dev.platform_data;
gpio_free(pdata->sensor_reset_gpio);
gpio_free(pdata->sensor_power_gpio);
+ mcam_deinit_clk(mcam);
iounmap(cam->power_regs);
iounmap(mcam->regs);
kfree(cam);
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/s3c-camif/camif-regs.c
index a9e3b16460b8..ebf5b184cce4 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/s3c-camif/camif-regs.c
@@ -106,15 +106,15 @@ static const u32 src_pixfmt_map[8][2] = {
void camif_hw_set_source_format(struct camif_dev *camif)
{
struct v4l2_mbus_framefmt *mf = &camif->mbus_fmt;
- unsigned int i = ARRAY_SIZE(src_pixfmt_map);
+ int i;
u32 cfg;
- while (i-- >= 0) {
+ for (i = ARRAY_SIZE(src_pixfmt_map) - 1; i >= 0; i--) {
if (src_pixfmt_map[i][0] == mf->code)
break;
}
-
- if (i == 0 && src_pixfmt_map[i][0] != mf->code) {
+ if (i < 0) {
+ i = 0;
dev_err(camif->dev,
"Unsupported pixel code, falling back to %#08x\n",
src_pixfmt_map[i][0]);
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
index 363a97cc7681..2398cdf61341 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
@@ -374,9 +374,9 @@
#define S5P_FIMV_NUM_PIXELS_IN_MB_COL_V6 16
/* Buffer size requirements defined by hardware */
-#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 1) * 8)
+#define S5P_FIMV_TMV_BUFFER_SIZE_V6(w, h) (((w) + 1) * ((h) + 3) * 8)
#define S5P_FIMV_ME_BUFFER_SIZE_V6(imw, imh, mbw, mbh) \
- ((DIV_ROUND_UP(imw, 64) * DIV_ROUND_UP(imh, 64) * 256) + \
+ (((((imw + 127) / 64) * 16) * DIV_ROUND_UP(imh, 64) * 256) + \
(DIV_ROUND_UP((mbw) * (mbh), 32) * 16))
#define S5P_FIMV_SCRATCH_BUF_SIZE_H264_DEC_V6(w, h) (((w) * 192) + 64)
#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(w, h) \
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h
new file mode 100644
index 000000000000..ea5ec2a711af
--- /dev/null
+++ b/drivers/media/platform/s5p-mfc/regs-mfc-v7.h
@@ -0,0 +1,61 @@
+/*
+ * Register definition file for Samsung MFC V7.x Interface (FIMV) driver
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.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.
+ */
+
+#ifndef _REGS_MFC_V7_H
+#define _REGS_MFC_V7_H
+
+#include "regs-mfc-v6.h"
+
+/* Additional features of v7 */
+#define S5P_FIMV_CODEC_VP8_ENC_V7 25
+
+/* Additional registers for v7 */
+#define S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7 0xf47c
+
+#define S5P_FIMV_E_SOURCE_FIRST_ADDR_V7 0xf9e0
+#define S5P_FIMV_E_SOURCE_SECOND_ADDR_V7 0xf9e4
+#define S5P_FIMV_E_SOURCE_THIRD_ADDR_V7 0xf9e8
+#define S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7 0xf9ec
+#define S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7 0xf9f0
+#define S5P_FIMV_E_SOURCE_THIRD_STRIDE_V7 0xf9f4
+
+#define S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7 0xfa70
+#define S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7 0xfa74
+
+#define S5P_FIMV_E_VP8_OPTIONS_V7 0xfdb0
+#define S5P_FIMV_E_VP8_FILTER_OPTIONS_V7 0xfdb4
+#define S5P_FIMV_E_VP8_GOLDEN_FRAME_OPTION_V7 0xfdb8
+#define S5P_FIMV_E_VP8_NUM_T_LAYER_V7 0xfdc4
+
+/* MFCv7 variant defines */
+#define MAX_FW_SIZE_V7 (SZ_1M) /* 1MB */
+#define MAX_CPB_SIZE_V7 (3 * SZ_1M) /* 3MB */
+#define MFC_VERSION_V7 0x72
+#define MFC_NUM_PORTS_V7 1
+
+#define MFC_LUMA_PAD_BYTES_V7 256
+#define MFC_CHROMA_PAD_BYTES_V7 128
+
+/* MFCv7 Context buffer sizes */
+#define MFC_CTX_BUF_SIZE_V7 (30 * SZ_1K) /* 30KB */
+#define MFC_H264_DEC_CTX_BUF_SIZE_V7 (2 * SZ_1M) /* 2MB */
+#define MFC_OTHER_DEC_CTX_BUF_SIZE_V7 (20 * SZ_1K) /* 20KB */
+#define MFC_H264_ENC_CTX_BUF_SIZE_V7 (100 * SZ_1K) /* 100KB */
+#define MFC_OTHER_ENC_CTX_BUF_SIZE_V7 (10 * SZ_1K) /* 10KB */
+
+/* Buffer size defines */
+#define S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(w, h) \
+ (SZ_1M + ((w) * 144) + (8192 * (h)) + 49216)
+
+#define S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(w, h) \
+ (((w) * 48) + (((w) + 1) / 2 * 128) + 144 + 8192)
+
+#endif /*_REGS_MFC_V7_H*/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c
index a130dcdb7206..084263dd126f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c
@@ -1391,6 +1391,32 @@ static struct s5p_mfc_variant mfc_drvdata_v6 = {
.fw_name = "s5p-mfc-v6.fw",
};
+struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = {
+ .dev_ctx = MFC_CTX_BUF_SIZE_V7,
+ .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7,
+ .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7,
+ .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7,
+ .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7,
+};
+
+struct s5p_mfc_buf_size buf_size_v7 = {
+ .fw = MAX_FW_SIZE_V7,
+ .cpb = MAX_CPB_SIZE_V7,
+ .priv = &mfc_buf_size_v7,
+};
+
+struct s5p_mfc_buf_align mfc_buf_align_v7 = {
+ .base = 0,
+};
+
+static struct s5p_mfc_variant mfc_drvdata_v7 = {
+ .version = MFC_VERSION_V7,
+ .port_num = MFC_NUM_PORTS_V7,
+ .buf_size = &buf_size_v7,
+ .buf_align = &mfc_buf_align_v7,
+ .fw_name = "s5p-mfc-v7.fw",
+};
+
static struct platform_device_id mfc_driver_ids[] = {
{
.name = "s5p-mfc",
@@ -1401,6 +1427,9 @@ static struct platform_device_id mfc_driver_ids[] = {
}, {
.name = "s5p-mfc-v6",
.driver_data = (unsigned long)&mfc_drvdata_v6,
+ }, {
+ .name = "s5p-mfc-v7",
+ .driver_data = (unsigned long)&mfc_drvdata_v7,
},
{},
};
@@ -1413,6 +1442,9 @@ static const struct of_device_id exynos_mfc_match[] = {
}, {
.compatible = "samsung,mfc-v6",
.data = &mfc_drvdata_v6,
+ }, {
+ .compatible = "samsung,mfc-v7",
+ .data = &mfc_drvdata_v7,
},
{},
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
index f0a41c95df84..242c033cf8bb 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
@@ -20,7 +20,7 @@ static struct s5p_mfc_hw_cmds *s5p_mfc_cmds;
void s5p_mfc_init_hw_cmds(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v6();
else
s5p_mfc_cmds = s5p_mfc_init_hw_cmds_v5();
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
index 5708fc3d9b4d..db796c8e7874 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -108,6 +108,9 @@ static int s5p_mfc_open_inst_cmd_v6(struct s5p_mfc_ctx *ctx)
case S5P_MFC_CODEC_H263_ENC:
codec_type = S5P_FIMV_CODEC_H263_ENC_V6;
break;
+ case S5P_MFC_CODEC_VP8_ENC:
+ codec_type = S5P_FIMV_CODEC_VP8_ENC_V7;
+ break;
default:
codec_type = S5P_FIMV_CODEC_NONE_V6;
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
index ef4074cd5316..6920b546181a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
@@ -24,6 +24,7 @@
#include <media/videobuf2-core.h>
#include "regs-mfc.h"
#include "regs-mfc-v6.h"
+#include "regs-mfc-v7.h"
/* Definitions related to MFC memory */
@@ -64,7 +65,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define MFC_ENC_CAP_PLANE_COUNT 1
#define MFC_ENC_OUT_PLANE_COUNT 2
#define STUFF_BYTE 4
-#define MFC_MAX_CTRLS 70
+#define MFC_MAX_CTRLS 77
#define S5P_MFC_CODEC_NONE -1
#define S5P_MFC_CODEC_H264_DEC 0
@@ -80,6 +81,7 @@ static inline dma_addr_t s5p_mfc_mem_cookie(void *a, void *b)
#define S5P_MFC_CODEC_H264_MVC_ENC 21
#define S5P_MFC_CODEC_MPEG4_ENC 22
#define S5P_MFC_CODEC_H263_ENC 23
+#define S5P_MFC_CODEC_VP8_ENC 24
#define S5P_MFC_R2H_CMD_EMPTY 0
#define S5P_MFC_R2H_CMD_SYS_INIT_RET 1
@@ -408,6 +410,21 @@ struct s5p_mfc_mpeg4_enc_params {
};
/**
+ * struct s5p_mfc_vp8_enc_params - encoding parameters for vp8
+ */
+struct s5p_mfc_vp8_enc_params {
+ u8 imd_4x4;
+ enum v4l2_vp8_num_partitions num_partitions;
+ enum v4l2_vp8_num_ref_frames num_ref;
+ u8 filter_level;
+ u8 filter_sharpness;
+ u32 golden_frame_ref_period;
+ enum v4l2_vp8_golden_frame_sel golden_frame_sel;
+ u8 hier_layer;
+ u8 hier_layer_qp[3];
+};
+
+/**
* struct s5p_mfc_enc_params - general encoding parameters
*/
struct s5p_mfc_enc_params {
@@ -441,6 +458,7 @@ struct s5p_mfc_enc_params {
struct {
struct s5p_mfc_h264_enc_params h264;
struct s5p_mfc_mpeg4_enc_params mpeg4;
+ struct s5p_mfc_vp8_enc_params vp8;
} codec;
};
@@ -683,6 +701,7 @@ void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx);
#define HAS_PORTNUM(dev) (dev ? (dev->variant ? \
(dev->variant->port_num ? 1 : 0) : 0) : 0)
#define IS_TWOPORT(dev) (dev->variant->port_num == 2 ? 1 : 0)
-#define IS_MFCV6(dev) (dev->variant->version >= 0x60 ? 1 : 0)
+#define IS_MFCV6_PLUS(dev) (dev->variant->version >= 0x60 ? 1 : 0)
+#define IS_MFCV7(dev) (dev->variant->version >= 0x70 ? 1 : 0)
#endif /* S5P_MFC_COMMON_H_ */
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
index dc1fc94a488d..7cab6849fb5b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
@@ -164,7 +164,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
mfc_debug_enter();
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
/* Reset IP */
/* except RISC, reset */
mfc_write(dev, 0xFEE, S5P_FIMV_MFC_RESET_V6);
@@ -213,7 +213,7 @@ int s5p_mfc_reset(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
mfc_write(dev, dev->bank1, S5P_FIMV_RISC_BASE_ADDRESS_V6);
mfc_debug(2, "Base Address : %08x\n", dev->bank1);
} else {
@@ -226,7 +226,7 @@ static inline void s5p_mfc_init_memctrl(struct s5p_mfc_dev *dev)
static inline void s5p_mfc_clear_cmds(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
/* Zero initialization should be done before RESET.
* Nothing to do here. */
} else {
@@ -264,7 +264,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clear_cmds(dev);
/* 3. Release reset signal to the RISC */
s5p_mfc_clean_dev_int_flags(dev);
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
@@ -301,7 +301,7 @@ int s5p_mfc_init_hw(struct s5p_mfc_dev *dev)
s5p_mfc_clock_off();
return -EIO;
}
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
ver = mfc_read(dev, S5P_FIMV_FW_VERSION_V6);
else
ver = mfc_read(dev, S5P_FIMV_FW_VERSION);
@@ -380,7 +380,7 @@ int s5p_mfc_wakeup(struct s5p_mfc_dev *dev)
return ret;
}
/* 4. Release reset signal to the RISC */
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
mfc_write(dev, 0x1, S5P_FIMV_RISC_ON_V6);
else
mfc_write(dev, 0x3ff, S5P_FIMV_SW_RESET);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
index 4f6dd42c9adb..8faf9691712d 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
@@ -386,7 +386,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unknown codec\n");
return -EINVAL;
}
- if (!IS_MFCV6(dev)) {
+ if (!IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_VP8) {
mfc_err("Not supported format.\n");
return -EINVAL;
@@ -398,10 +398,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
mfc_err("Unsupported format for destination.\n");
return -EINVAL;
}
- if (IS_MFCV6(dev) && (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
+ if (IS_MFCV6_PLUS(dev) &&
+ (fmt->fourcc == V4L2_PIX_FMT_NV12MT)) {
mfc_err("Not supported format.\n");
return -EINVAL;
- } else if (!IS_MFCV6(dev) &&
+ } else if (!IS_MFCV6_PLUS(dev) &&
(fmt->fourcc != V4L2_PIX_FMT_NV12MT)) {
mfc_err("Not supported format.\n");
return -EINVAL;
@@ -925,7 +926,7 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
else
@@ -1050,7 +1051,7 @@ static int s5p_mfc_stop_streaming(struct vb2_queue *q)
ctx->dpb_flush_flag = 1;
ctx->dec_dst_flag = 0;
spin_unlock_irqrestore(&dev->irqlock, flags);
- if (IS_MFCV6(dev) && (ctx->state == MFCINST_RUNNING)) {
+ if (IS_MFCV6_PLUS(dev) && (ctx->state == MFCINST_RUNNING)) {
ctx->state = MFCINST_FLUSH;
set_work_bit_irqsave(ctx);
s5p_mfc_clean_ctx_int_flags(ctx);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index 59e56f4c8ce3..41f5a3c10dbd 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -84,6 +84,13 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_ENC,
.num_planes = 1,
},
+ {
+ .name = "VP8 Encoded Stream",
+ .fourcc = V4L2_PIX_FMT_VP8,
+ .codec_mode = S5P_MFC_CODEC_VP8_ENC,
+ .type = MFC_FMT_ENC,
+ .num_planes = 1,
+ },
};
#define NUM_FORMATS ARRAY_SIZE(formats)
@@ -557,6 +564,60 @@ static struct mfc_control controls[] = {
.step = 1,
.default_value = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS,
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES,
+ .type = V4L2_CTRL_TYPE_INTEGER_MENU,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_2_REF_FRAME,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_1_REF_FRAME,
+ .menu_skip_mask = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 63,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = 7,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0,
+ .maximum = (1 << 16) - 1,
+ .step = 1,
+ .default_value = 0,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL,
+ .type = V4L2_CTRL_TYPE_MENU,
+ .minimum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
+ .maximum = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_REF_PERIOD,
+ .default_value = V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_USE_PREV,
+ .menu_skip_mask = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(controls)
@@ -663,7 +724,7 @@ static int enc_post_seq_start(struct s5p_mfc_ctx *ctx)
spin_unlock_irqrestore(&dev->irqlock, flags);
}
- if (!IS_MFCV6(dev)) {
+ if (!IS_MFCV6_PLUS(dev)) {
ctx->state = MFCINST_RUNNING;
if (s5p_mfc_ctx_ready(ctx))
set_work_bit_irqsave(ctx);
@@ -917,6 +978,11 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
return -EINVAL;
}
+ if (!IS_MFCV7(dev) && (fmt->fourcc == V4L2_PIX_FMT_VP8)) {
+ mfc_err("VP8 is supported only in MFC v7\n");
+ return -EINVAL;
+ }
+
if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) {
mfc_err("must be set encoding output size\n");
return -EINVAL;
@@ -931,12 +997,12 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
return -EINVAL;
}
- if (!IS_MFCV6(dev)) {
+ if (!IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16) {
mfc_err("Not supported format.\n");
return -EINVAL;
}
- } else if (IS_MFCV6(dev)) {
+ } else if (IS_MFCV6_PLUS(dev)) {
if (fmt->fourcc == V4L2_PIX_FMT_NV12MT) {
mfc_err("Not supported format.\n");
return -EINVAL;
@@ -1060,7 +1126,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
return -EINVAL;
}
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
/* Check for min encoder buffers */
if (ctx->pb_count &&
(reqbufs->count < ctx->pb_count)) {
@@ -1341,7 +1407,7 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
S5P_FIMV_ENC_PROFILE_H264_BASELINE;
break;
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
- if (IS_MFCV6(dev))
+ if (IS_MFCV6_PLUS(dev))
p->codec.h264.profile =
S5P_FIMV_ENC_PROFILE_H264_CONSTRAINED_BASELINE;
else
@@ -1470,6 +1536,27 @@ static int s5p_mfc_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
p->codec.mpeg4.quarter_pixel = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_VPX_NUM_PARTITIONS:
+ p->codec.vp8.num_partitions = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_IMD_DISABLE_4X4:
+ p->codec.vp8.imd_4x4 = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_NUM_REF_FRAMES:
+ p->codec.vp8.num_ref = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_FILTER_LEVEL:
+ p->codec.vp8.filter_level = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_FILTER_SHARPNESS:
+ p->codec.vp8.filter_sharpness = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_REF_PERIOD:
+ p->codec.vp8.golden_frame_ref_period = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL:
+ p->codec.vp8.golden_frame_sel = ctrl->val;
+ break;
default:
v4l2_err(&dev->v4l2_dev, "Invalid control, id=%d, val=%d\n",
ctrl->id, ctrl->val);
@@ -1650,9 +1737,11 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
*buf_count = 1;
if (*buf_count > MFC_MAX_BUFFERS)
*buf_count = MFC_MAX_BUFFERS;
+
psize[0] = ctx->luma_size;
psize[1] = ctx->chroma_size;
- if (IS_MFCV6(dev)) {
+
+ if (IS_MFCV6_PLUS(dev)) {
allocators[0] =
ctx->dev->alloc_ctx[MFC_BANK1_ALLOC_CTX];
allocators[1] =
@@ -1761,7 +1850,8 @@ static int s5p_mfc_start_streaming(struct vb2_queue *q, unsigned int count)
struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
- if (IS_MFCV6(dev) && (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
+ if (IS_MFCV6_PLUS(dev) &&
+ (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
if ((ctx->state == MFCINST_GOT_INST) &&
(dev->curr_ctx == ctx->num) && dev->hw_lock) {
@@ -1915,7 +2005,9 @@ int s5p_mfc_enc_ctrls_setup(struct s5p_mfc_ctx *ctx)
ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->ctrl_handler,
&cfg, NULL);
} else {
- if (controls[i].type == V4L2_CTRL_TYPE_MENU) {
+ if ((controls[i].type == V4L2_CTRL_TYPE_MENU) ||
+ (controls[i].type ==
+ V4L2_CTRL_TYPE_INTEGER_MENU)) {
ctx->ctrls[i] = v4l2_ctrl_new_std_menu(
&ctx->ctrl_handler,
&s5p_mfc_enc_ctrl_ops, controls[i].id,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
index 10f8ac37cecd..3c01c339d696 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -21,7 +21,7 @@ static struct s5p_mfc_hw_ops *s5p_mfc_ops;
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev)
{
- if (IS_MFCV6(dev)) {
+ if (IS_MFCV6_PLUS(dev)) {
s5p_mfc_ops = s5p_mfc_init_hw_ops_v6();
dev->warn_start = S5P_FIMV_ERR_WARNINGS_START_V6;
} else {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
index 66f0d042357f..461358c4a790 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -80,6 +80,7 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->tmv_buffer_size = S5P_FIMV_NUM_TMV_BUFFERS_V6 *
ALIGN(S5P_FIMV_TMV_BUFFER_SIZE_V6(mb_width, mb_height),
S5P_FIMV_TMV_BUFFER_ALIGN_V6);
+
ctx->luma_dpb_size = ALIGN((mb_width * mb_height) *
S5P_FIMV_LUMA_MB_TO_PIXEL_V6,
S5P_FIMV_LUMA_DPB_BUFFER_ALIGN_V6);
@@ -112,10 +113,18 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
(ctx->mv_count * ctx->mv_size);
break;
case S5P_MFC_CODEC_MPEG4_DEC:
- ctx->scratch_buf_size =
- S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
- mb_width,
- mb_height);
+ if (IS_MFCV7(dev)) {
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V7(
+ mb_width,
+ mb_height);
+ } else {
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_MPEG4_DEC_V6(
+ mb_width,
+ mb_height);
+ }
+
ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
ctx->bank1.size = ctx->scratch_buf_size;
@@ -179,6 +188,19 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
ctx->chroma_dpb_size + ctx->me_buffer_size));
ctx->bank2.size = 0;
break;
+ case S5P_MFC_CODEC_VP8_ENC:
+ ctx->scratch_buf_size =
+ S5P_FIMV_SCRATCH_BUF_SIZE_VP8_ENC_V7(
+ mb_width,
+ mb_height);
+ ctx->scratch_buf_size = ALIGN(ctx->scratch_buf_size,
+ S5P_FIMV_SCRATCH_BUFFER_ALIGN_V6);
+ ctx->bank1.size =
+ ctx->scratch_buf_size + ctx->tmv_buffer_size +
+ (ctx->pb_count * (ctx->luma_dpb_size +
+ ctx->chroma_dpb_size + ctx->me_buffer_size));
+ ctx->bank2.size = 0;
+ break;
default:
break;
}
@@ -228,6 +250,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
break;
case S5P_MFC_CODEC_MPEG4_ENC:
case S5P_MFC_CODEC_H263_ENC:
+ case S5P_MFC_CODEC_VP8_ENC:
ctx->ctx.size = buf_size->other_enc_ctx;
break;
default:
@@ -329,6 +352,12 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
ctx->buf_width = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
ctx->luma_size = ALIGN((mb_width * mb_height) * 256, 256);
ctx->chroma_size = ALIGN((mb_width * mb_height) * 128, 256);
+
+ /* MFCv7 needs pad bytes for Luma and Chroma */
+ if (IS_MFCV7(ctx->dev)) {
+ ctx->luma_size += MFC_LUMA_PAD_BYTES_V7;
+ ctx->chroma_size += MFC_CHROMA_PAD_BYTES_V7;
+ }
}
/* Set registers for decoding stream buffer */
@@ -453,8 +482,13 @@ static void s5p_mfc_set_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
{
struct s5p_mfc_dev *dev = ctx->dev;
- WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6); /* 256B align */
- WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
+ if (IS_MFCV7(dev)) {
+ WRITEL(y_addr, S5P_FIMV_E_SOURCE_FIRST_ADDR_V7);
+ WRITEL(c_addr, S5P_FIMV_E_SOURCE_SECOND_ADDR_V7);
+ } else {
+ WRITEL(y_addr, S5P_FIMV_E_SOURCE_LUMA_ADDR_V6);
+ WRITEL(c_addr, S5P_FIMV_E_SOURCE_CHROMA_ADDR_V6);
+ }
mfc_debug(2, "enc src y buf addr: 0x%08lx\n", y_addr);
mfc_debug(2, "enc src c buf addr: 0x%08lx\n", c_addr);
@@ -466,8 +500,13 @@ static void s5p_mfc_get_enc_frame_buffer_v6(struct s5p_mfc_ctx *ctx,
struct s5p_mfc_dev *dev = ctx->dev;
unsigned long enc_recon_y_addr, enc_recon_c_addr;
- *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
- *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
+ if (IS_MFCV7(dev)) {
+ *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_FIRST_ADDR_V7);
+ *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_SECOND_ADDR_V7);
+ } else {
+ *y_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_LUMA_ADDR_V6);
+ *c_addr = READL(S5P_FIMV_E_ENCODED_SOURCE_CHROMA_ADDR_V6);
+ }
enc_recon_y_addr = READL(S5P_FIMV_E_RECON_LUMA_DPB_ADDR_V6);
enc_recon_c_addr = READL(S5P_FIMV_E_RECON_CHROMA_DPB_ADDR_V6);
@@ -1140,6 +1179,80 @@ static int s5p_mfc_set_enc_params_h263(struct s5p_mfc_ctx *ctx)
return 0;
}
+static int s5p_mfc_set_enc_params_vp8(struct s5p_mfc_ctx *ctx)
+{
+ struct s5p_mfc_dev *dev = ctx->dev;
+ struct s5p_mfc_enc_params *p = &ctx->enc_params;
+ struct s5p_mfc_vp8_enc_params *p_vp8 = &p->codec.vp8;
+ unsigned int reg = 0;
+ unsigned int val = 0;
+
+ mfc_debug_enter();
+
+ s5p_mfc_set_enc_params(ctx);
+
+ /* pictype : number of B */
+ reg = READL(S5P_FIMV_E_GOP_CONFIG_V6);
+ reg &= ~(0x3 << 16);
+ reg |= ((p->num_b_frame & 0x3) << 16);
+ WRITEL(reg, S5P_FIMV_E_GOP_CONFIG_V6);
+
+ /* profile & level */
+ reg = 0;
+ /** profile */
+ reg |= (0x1 << 4);
+ WRITEL(reg, S5P_FIMV_E_PICTURE_PROFILE_V6);
+
+ /* rate control config. */
+ reg = READL(S5P_FIMV_E_RC_CONFIG_V6);
+ /** macroblock level rate control */
+ reg &= ~(0x1 << 8);
+ reg |= ((p->rc_mb & 0x1) << 8);
+ WRITEL(reg, S5P_FIMV_E_RC_CONFIG_V6);
+
+ /* frame rate */
+ if (p->rc_frame && p->rc_framerate_num && p->rc_framerate_denom) {
+ reg = 0;
+ reg |= ((p->rc_framerate_num & 0xFFFF) << 16);
+ reg |= p->rc_framerate_denom & 0xFFFF;
+ WRITEL(reg, S5P_FIMV_E_RC_FRAME_RATE_V6);
+ }
+
+ /* vbv buffer size */
+ if (p->frame_skip_mode ==
+ V4L2_MPEG_MFC51_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT) {
+ WRITEL(p->vbv_size & 0xFFFF, S5P_FIMV_E_VBV_BUFFER_SIZE_V6);
+
+ if (p->rc_frame)
+ WRITEL(p->vbv_delay, S5P_FIMV_E_VBV_INIT_DELAY_V6);
+ }
+
+ /* VP8 specific params */
+ reg = 0;
+ reg |= (p_vp8->imd_4x4 & 0x1) << 10;
+ switch (p_vp8->num_partitions) {
+ case V4L2_CID_MPEG_VIDEO_VPX_1_PARTITION:
+ val = 0;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_2_PARTITIONS:
+ val = 2;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_4_PARTITIONS:
+ val = 4;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VPX_8_PARTITIONS:
+ val = 8;
+ break;
+ }
+ reg |= (val & 0xF) << 3;
+ reg |= (p_vp8->num_ref & 0x2);
+ WRITEL(reg, S5P_FIMV_E_VP8_OPTIONS_V7);
+
+ mfc_debug_leave();
+
+ return 0;
+}
+
/* Initialize decoding */
static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
{
@@ -1166,6 +1279,12 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6);
WRITEL(ctx->display_delay, S5P_FIMV_D_DISPLAY_DELAY_V6);
}
+
+ if (IS_MFCV7(dev)) {
+ WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
+ reg = 0;
+ }
+
/* Setup loop filter, for decoding this is only valid for MPEG4 */
if (ctx->codec_mode == S5P_MFC_CODEC_MPEG4_DEC) {
mfc_debug(2, "Set loop filter to: %d\n",
@@ -1176,7 +1295,10 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV12MT_16X16)
reg |= (0x1 << S5P_FIMV_D_OPT_TILE_MODE_SHIFT_V6);
- WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
+ if (IS_MFCV7(dev))
+ WRITEL(reg, S5P_FIMV_D_INIT_BUFFER_OPTIONS_V7);
+ else
+ WRITEL(reg, S5P_FIMV_D_DEC_OPTIONS_V6);
/* 0: NV12(CbCr), 1: NV21(CrCb) */
if (ctx->dst_fmt->fourcc == V4L2_PIX_FMT_NV21M)
@@ -1184,6 +1306,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx)
else
WRITEL(0x0, S5P_FIMV_PIXEL_FORMAT_V6);
+
/* sei parse */
WRITEL(ctx->sei_fp_parse & 0x1, S5P_FIMV_D_SEI_ENABLE_V6);
@@ -1248,12 +1371,20 @@ static int s5p_mfc_init_encode_v6(struct s5p_mfc_ctx *ctx)
s5p_mfc_set_enc_params_mpeg4(ctx);
else if (ctx->codec_mode == S5P_MFC_CODEC_H263_ENC)
s5p_mfc_set_enc_params_h263(ctx);
+ else if (ctx->codec_mode == S5P_MFC_CODEC_VP8_ENC)
+ s5p_mfc_set_enc_params_vp8(ctx);
else {
mfc_err("Unknown codec for encoding (%x).\n",
ctx->codec_mode);
return -EINVAL;
}
+ /* Set stride lengths */
+ if (IS_MFCV7(dev)) {
+ WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_FIRST_STRIDE_V7);
+ WRITEL(ctx->img_width, S5P_FIMV_E_SOURCE_SECOND_STRIDE_V7);
+ }
+
WRITEL(ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc, dev,
S5P_FIMV_CH_SEQ_HEADER_V6, NULL);
diff --git a/drivers/media/platform/s5p-tv/hdmi_drv.c b/drivers/media/platform/s5p-tv/hdmi_drv.c
index 1b34c3629858..534722c04ec5 100644
--- a/drivers/media/platform/s5p-tv/hdmi_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmi_drv.c
@@ -37,6 +37,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
#include "regs-hdmi.h"
@@ -625,7 +626,7 @@ static int hdmi_s_dv_timings(struct v4l2_subdev *sd,
int i;
for (i = 0; i < ARRAY_SIZE(hdmi_timings); i++)
- if (v4l_match_dv_timings(&hdmi_timings[i].dv_timings,
+ if (v4l2_match_dv_timings(&hdmi_timings[i].dv_timings,
timings, 0))
break;
if (i == ARRAY_SIZE(hdmi_timings)) {
diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig
index 626dcccc37da..af39c4665554 100644
--- a/drivers/media/platform/soc_camera/Kconfig
+++ b/drivers/media/platform/soc_camera/Kconfig
@@ -44,6 +44,14 @@ config VIDEO_PXA27x
---help---
This is a v4l2 driver for the PXA27x Quick Capture Interface
+config VIDEO_RCAR_VIN
+ tristate "R-Car Video Input (VIN) support"
+ depends on VIDEO_DEV && SOC_CAMERA
+ select VIDEOBUF2_DMA_CONTIG
+ select SOC_CAMERA_SCALE_CROP
+ ---help---
+ This is a v4l2 driver for the R-Car VIN Interface
+
config VIDEO_SH_MOBILE_CSI2
tristate "SuperH Mobile MIPI CSI-2 Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK
diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile
index 39186224c16a..8aed26d7a64d 100644
--- a/drivers/media/platform/soc_camera/Makefile
+++ b/drivers/media/platform/soc_camera/Makefile
@@ -14,3 +14,4 @@ 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
+obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar_vin.o
diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c
index 1047e3e8db77..8f9f6211c52e 100644
--- a/drivers/media/platform/soc_camera/mx3_camera.c
+++ b/drivers/media/platform/soc_camera/mx3_camera.c
@@ -672,7 +672,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
fmt = soc_mbus_get_fmtdesc(code);
if (!fmt) {
dev_warn(icd->parent,
- "Unsupported format code #%u: %d\n", idx, code);
+ "Unsupported format code #%u: 0x%x\n", idx, code);
return 0;
}
@@ -688,7 +688,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[0];
xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[0].name, code);
}
break;
@@ -698,7 +698,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, unsigned int id
xlate->host_fmt = &mx3_camera_formats[1];
xlate->code = code;
xlate++;
- dev_dbg(dev, "Providing format %s using code %d\n",
+ dev_dbg(dev, "Providing format %s using code 0x%x\n",
mx3_camera_formats[1].name, code);
}
break;
@@ -1144,6 +1144,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
static int mx3_camera_probe(struct platform_device *pdev)
{
+ struct mx3_camera_pdata *pdata = pdev->dev.platform_data;
struct mx3_camera_dev *mx3_cam;
struct resource *res;
void __iomem *base;
@@ -1151,26 +1152,25 @@ static int mx3_camera_probe(struct platform_device *pdev)
struct soc_camera_host *soc_host;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- err = -ENODEV;
- goto egetres;
- }
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ if (!pdata)
+ return -EINVAL;
- mx3_cam = vzalloc(sizeof(*mx3_cam));
+ mx3_cam = devm_kzalloc(&pdev->dev, sizeof(*mx3_cam), GFP_KERNEL);
if (!mx3_cam) {
dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
- err = -ENOMEM;
- goto ealloc;
+ return -ENOMEM;
}
- mx3_cam->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(mx3_cam->clk)) {
- err = PTR_ERR(mx3_cam->clk);
- goto eclkget;
- }
+ mx3_cam->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mx3_cam->clk))
+ return PTR_ERR(mx3_cam->clk);
- mx3_cam->pdata = pdev->dev.platform_data;
- mx3_cam->platform_flags = mx3_cam->pdata->flags;
+ mx3_cam->pdata = pdata;
+ mx3_cam->platform_flags = pdata->flags;
if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_MASK)) {
/*
* Platform hasn't set available data widths. This is bad.
@@ -1189,7 +1189,7 @@ static int mx3_camera_probe(struct platform_device *pdev)
if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
mx3_cam->width_flags |= 1 << 14;
- mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
+ mx3_cam->mclk = pdata->mclk_10khz * 10000;
if (!mx3_cam->mclk) {
dev_warn(&pdev->dev,
"mclk_10khz == 0! Please, fix your platform data. "
@@ -1201,13 +1201,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&mx3_cam->capture);
spin_lock_init(&mx3_cam->lock);
- base = ioremap(res->start, resource_size(res));
- if (!base) {
- pr_err("Couldn't map %x@%x\n", resource_size(res), res->start);
- err = -ENOMEM;
- goto eioremap;
- }
-
mx3_cam->base = base;
soc_host = &mx3_cam->soc_host;
@@ -1218,9 +1211,12 @@ static int mx3_camera_probe(struct platform_device *pdev)
soc_host->nr = pdev->id;
mx3_cam->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
- if (IS_ERR(mx3_cam->alloc_ctx)) {
- err = PTR_ERR(mx3_cam->alloc_ctx);
- goto eallocctx;
+ if (IS_ERR(mx3_cam->alloc_ctx))
+ return PTR_ERR(mx3_cam->alloc_ctx);
+
+ if (pdata->asd_sizes) {
+ soc_host->asd = pdata->asd;
+ soc_host->asd_sizes = pdata->asd_sizes;
}
err = soc_camera_host_register(soc_host);
@@ -1234,14 +1230,6 @@ static int mx3_camera_probe(struct platform_device *pdev)
ecamhostreg:
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
-eallocctx:
- iounmap(base);
-eioremap:
- clk_put(mx3_cam->clk);
-eclkget:
- vfree(mx3_cam);
-ealloc:
-egetres:
return err;
}
@@ -1251,12 +1239,8 @@ static int mx3_camera_remove(struct platform_device *pdev)
struct mx3_camera_dev *mx3_cam = container_of(soc_host,
struct mx3_camera_dev, soc_host);
- clk_put(mx3_cam->clk);
-
soc_camera_host_unregister(soc_host);
- iounmap(mx3_cam->base);
-
/*
* The channel has either not been allocated,
* or should have been released
@@ -1266,8 +1250,6 @@ static int mx3_camera_remove(struct platform_device *pdev)
vb2_dma_contig_cleanup_ctx(mx3_cam->alloc_ctx);
- vfree(mx3_cam);
-
dmaengine_put();
return 0;
@@ -1276,6 +1258,7 @@ static int mx3_camera_remove(struct platform_device *pdev)
static struct platform_driver mx3_camera_driver = {
.driver = {
.name = MX3_CAM_DRV_NAME,
+ .owner = THIS_MODULE,
},
.probe = mx3_camera_probe,
.remove = mx3_camera_remove,
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
new file mode 100644
index 000000000000..d02a7e0b773f
--- /dev/null
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -0,0 +1,1486 @@
+/*
+ * SoC-camera host driver for Renesas R-Car VIN unit
+ *
+ * Copyright (C) 2011-2013 Renesas Solutions Corp.
+ * Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
+ *
+ * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c"
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * 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/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/camera-rcar.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/soc_camera.h>
+#include <media/soc_mediabus.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "soc_scale_crop.h"
+
+#define DRV_NAME "rcar_vin"
+
+/* Register offsets for R-Car VIN */
+#define VNMC_REG 0x00 /* Video n Main Control Register */
+#define VNMS_REG 0x04 /* Video n Module Status Register */
+#define VNFC_REG 0x08 /* Video n Frame Capture Register */
+#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */
+#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */
+#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */
+#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */
+#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */
+#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */
+#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */
+#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */
+#define VNIS_REG 0x2C /* Video n Image Stride Register */
+#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */
+#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */
+#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */
+#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */
+#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */
+#define VNYS_REG 0x50 /* Video n Y Scale Register */
+#define VNXS_REG 0x54 /* Video n X Scale Register */
+#define VNDMR_REG 0x58 /* Video n Data Mode Register */
+#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */
+#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */
+
+/* Register bit fields for R-Car VIN */
+/* Video n Main Control Register bits */
+#define VNMC_FOC (1 << 21)
+#define VNMC_YCAL (1 << 19)
+#define VNMC_INF_YUV8_BT656 (0 << 16)
+#define VNMC_INF_YUV8_BT601 (1 << 16)
+#define VNMC_INF_YUV16 (5 << 16)
+#define VNMC_VUP (1 << 10)
+#define VNMC_IM_ODD (0 << 3)
+#define VNMC_IM_ODD_EVEN (1 << 3)
+#define VNMC_IM_EVEN (2 << 3)
+#define VNMC_IM_FULL (3 << 3)
+#define VNMC_BPS (1 << 1)
+#define VNMC_ME (1 << 0)
+
+/* Video n Module Status Register bits */
+#define VNMS_FBS_MASK (3 << 3)
+#define VNMS_FBS_SHIFT 3
+#define VNMS_AV (1 << 1)
+#define VNMS_CA (1 << 0)
+
+/* Video n Frame Capture Register bits */
+#define VNFC_C_FRAME (1 << 1)
+#define VNFC_S_FRAME (1 << 0)
+
+/* Video n Interrupt Enable Register bits */
+#define VNIE_FIE (1 << 4)
+#define VNIE_EFE (1 << 1)
+
+/* Video n Data Mode Register bits */
+#define VNDMR_EXRGB (1 << 8)
+#define VNDMR_BPSM (1 << 4)
+#define VNDMR_DTMD_YCSEP (1 << 1)
+#define VNDMR_DTMD_ARGB1555 (1 << 0)
+
+/* Video n Data Mode Register 2 bits */
+#define VNDMR2_VPS (1 << 30)
+#define VNDMR2_HPS (1 << 29)
+#define VNDMR2_FTEV (1 << 17)
+
+#define VIN_MAX_WIDTH 2048
+#define VIN_MAX_HEIGHT 2048
+
+enum chip_id {
+ RCAR_H1,
+ RCAR_M1,
+ RCAR_E1,
+};
+
+enum rcar_vin_state {
+ STOPPED = 0,
+ RUNNING,
+ STOPPING,
+};
+
+struct rcar_vin_priv {
+ void __iomem *base;
+ spinlock_t lock;
+ int sequence;
+ /* State of the VIN module in capturing mode */
+ enum rcar_vin_state state;
+ struct rcar_vin_platform_data *pdata;
+ struct soc_camera_host ici;
+ struct list_head capture;
+#define MAX_BUFFER_NUM 3
+ struct vb2_buffer *queue_buf[MAX_BUFFER_NUM];
+ struct vb2_alloc_ctx *alloc_ctx;
+ enum v4l2_field field;
+ unsigned int vb_count;
+ unsigned int nr_hw_slots;
+ bool request_to_stop;
+ struct completion capture_stop;
+ enum chip_id chip;
+};
+
+#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM)
+
+struct rcar_vin_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \
+ struct rcar_vin_buffer, \
+ vb)->list)
+
+struct rcar_vin_cam {
+ /* VIN offsets within the camera output, before the VIN scaler */
+ unsigned int vin_left;
+ unsigned int vin_top;
+ /* Client output, as seen by the VIN */
+ unsigned int width;
+ unsigned int height;
+ /*
+ * User window from S_CROP / G_CROP, produced by client cropping and
+ * scaling, VIN scaling and VIN cropping, mapped back onto the client
+ * input window
+ */
+ struct v4l2_rect subrect;
+ /* Camera cropping rectangle */
+ struct v4l2_rect rect;
+ const struct soc_mbus_pixelfmt *extra_fmt;
+};
+
+/*
+ * .queue_setup() is called to check whether the driver can accept the requested
+ * number of buffers and to fill in plane sizes for the current frame format if
+ * required
+ */
+static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count,
+ unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+
+ if (fmt) {
+ const struct soc_camera_format_xlate *xlate;
+ unsigned int bytes_per_line;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd,
+ fmt->fmt.pix.pixelformat);
+ if (!xlate)
+ return -EINVAL;
+ ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width,
+ xlate->host_fmt);
+ if (ret < 0)
+ return ret;
+
+ bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret);
+
+ ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line,
+ fmt->fmt.pix.height);
+ if (ret < 0)
+ return ret;
+
+ sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret);
+ } else {
+ /* Called from VIDIOC_REQBUFS or in compatibility mode */
+ sizes[0] = icd->sizeimage;
+ }
+
+ alloc_ctxs[0] = priv->alloc_ctx;
+
+ if (!vq->num_buffers)
+ priv->sequence = 0;
+
+ if (!*count)
+ *count = 2;
+ priv->vb_count = *count;
+
+ *num_planes = 1;
+
+ /* Number of hardware slots */
+ if (is_continuous_transfer(priv))
+ priv->nr_hw_slots = MAX_BUFFER_NUM;
+ else
+ priv->nr_hw_slots = 1;
+
+ dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]);
+
+ return 0;
+}
+
+static int rcar_vin_setup(struct rcar_vin_priv *priv)
+{
+ struct soc_camera_device *icd = priv->ici.icd;
+ struct rcar_vin_cam *cam = icd->host_priv;
+ u32 vnmc, dmr, interrupts;
+ bool progressive = false, output_is_yuv = false;
+
+ switch (priv->field) {
+ case V4L2_FIELD_TOP:
+ vnmc = VNMC_IM_ODD;
+ break;
+ case V4L2_FIELD_BOTTOM:
+ vnmc = VNMC_IM_EVEN;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ vnmc = VNMC_IM_FULL;
+ break;
+ case V4L2_FIELD_INTERLACED_BT:
+ vnmc = VNMC_IM_FULL | VNMC_FOC;
+ break;
+ case V4L2_FIELD_NONE:
+ if (is_continuous_transfer(priv)) {
+ vnmc = VNMC_IM_ODD_EVEN;
+ progressive = true;
+ } else {
+ vnmc = VNMC_IM_ODD;
+ }
+ break;
+ default:
+ vnmc = VNMC_IM_ODD;
+ break;
+ }
+
+ /* input interface */
+ switch (icd->current_fmt->code) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ /* BT.601/BT.1358 16bit YCbCr422 */
+ vnmc |= VNMC_INF_YUV16;
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */
+ vnmc |= priv->pdata->flags & RCAR_VIN_BT656 ?
+ VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601;
+ default:
+ break;
+ }
+
+ /* output format */
+ switch (icd->current_fmt->host_fmt->fourcc) {
+ case V4L2_PIX_FMT_NV16:
+ iowrite32(ALIGN(cam->width * cam->height, 0x80),
+ priv->base + VNUVAOF_REG);
+ dmr = VNDMR_DTMD_YCSEP;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ dmr = VNDMR_BPSM;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ dmr = 0;
+ output_is_yuv = true;
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ dmr = VNDMR_DTMD_ARGB1555;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ dmr = 0;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ if (priv->chip == RCAR_H1 || priv->chip == RCAR_E1) {
+ dmr = VNDMR_EXRGB;
+ break;
+ }
+ default:
+ dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n",
+ icd->current_fmt->host_fmt->fourcc);
+ return -EINVAL;
+ }
+
+ /* Always update on field change */
+ vnmc |= VNMC_VUP;
+
+ /* If input and output use the same colorspace, use bypass mode */
+ if (output_is_yuv)
+ vnmc |= VNMC_BPS;
+
+ /* progressive or interlaced mode */
+ interrupts = progressive ? VNIE_FIE | VNIE_EFE : VNIE_EFE;
+
+ /* ack interrupts */
+ iowrite32(interrupts, priv->base + VNINTS_REG);
+ /* enable interrupts */
+ iowrite32(interrupts, priv->base + VNIE_REG);
+ /* start capturing */
+ iowrite32(dmr, priv->base + VNDMR_REG);
+ iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG);
+
+ return 0;
+}
+
+static void rcar_vin_capture(struct rcar_vin_priv *priv)
+{
+ if (is_continuous_transfer(priv))
+ /* Continuous Frame Capture Mode */
+ iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG);
+ else
+ /* Single Frame Capture Mode */
+ iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG);
+}
+
+static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv)
+{
+ priv->state = STOPPING;
+
+ /* set continuous & single transfer off */
+ iowrite32(0, priv->base + VNFC_REG);
+ /* disable capture (release DMA buffer), reset */
+ iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME,
+ priv->base + VNMC_REG);
+
+ /* update the status if stopped already */
+ if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA))
+ priv->state = STOPPED;
+}
+
+static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv)
+{
+ int slot;
+
+ for (slot = 0; slot < priv->nr_hw_slots; slot++)
+ if (priv->queue_buf[slot] == NULL)
+ return slot;
+
+ return -1;
+}
+
+static int rcar_vin_hw_ready(struct rcar_vin_priv *priv)
+{
+ /* Ensure all HW slots are filled */
+ return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0;
+}
+
+/* Moves a buffer from the queue to the HW slots */
+static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv)
+{
+ struct vb2_buffer *vb;
+ dma_addr_t phys_addr_top;
+ int slot;
+
+ if (list_empty(&priv->capture))
+ return 0;
+
+ /* Find a free HW slot */
+ slot = rcar_vin_get_free_hw_slot(priv);
+ if (slot < 0)
+ return 0;
+
+ vb = &list_entry(priv->capture.next, struct rcar_vin_buffer, list)->vb;
+ list_del_init(to_buf_list(vb));
+ priv->queue_buf[slot] = vb;
+ phys_addr_top = vb2_dma_contig_plane_dma_addr(vb, 0);
+ iowrite32(phys_addr_top, priv->base + VNMB_REG(slot));
+
+ return 1;
+}
+
+static void rcar_vin_videobuf_queue(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned long size;
+
+ size = icd->sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n",
+ vb->v4l2_buf.index, vb2_plane_size(vb, 0), size);
+ goto error;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
+
+ spin_lock_irq(&priv->lock);
+
+ list_add_tail(to_buf_list(vb), &priv->capture);
+ rcar_vin_fill_hw_slot(priv);
+
+ /* If we weren't running, and have enough buffers, start capturing! */
+ if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) {
+ if (rcar_vin_setup(priv)) {
+ /* Submit error */
+ list_del_init(to_buf_list(vb));
+ spin_unlock_irq(&priv->lock);
+ goto error;
+ }
+ priv->request_to_stop = false;
+ init_completion(&priv->capture_stop);
+ priv->state = RUNNING;
+ rcar_vin_capture(priv);
+ }
+
+ spin_unlock_irq(&priv->lock);
+
+ return;
+
+error:
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void rcar_vin_videobuf_release(struct vb2_buffer *vb)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned int i;
+ int buf_in_use = 0;
+
+ spin_lock_irq(&priv->lock);
+
+ /* Is the buffer in use by the VIN hardware? */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i] == vb) {
+ buf_in_use = 1;
+ break;
+ }
+ }
+
+ if (buf_in_use) {
+ while (priv->state != STOPPED) {
+
+ /* issue stop if running */
+ if (priv->state == RUNNING)
+ rcar_vin_request_capture_stop(priv);
+
+ /* wait until capturing has been stopped */
+ if (priv->state == STOPPING) {
+ priv->request_to_stop = true;
+ spin_unlock_irq(&priv->lock);
+ wait_for_completion(&priv->capture_stop);
+ spin_lock_irq(&priv->lock);
+ }
+ }
+ /*
+ * Capturing has now stopped. The buffer we have been asked
+ * to release could be any of the current buffers in use, so
+ * release all buffers that are in use by HW
+ */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i]) {
+ vb2_buffer_done(priv->queue_buf[i],
+ VB2_BUF_STATE_ERROR);
+ priv->queue_buf[i] = NULL;
+ }
+ }
+ } else {
+ list_del_init(to_buf_list(vb));
+ }
+
+ spin_unlock_irq(&priv->lock);
+}
+
+static int rcar_vin_videobuf_init(struct vb2_buffer *vb)
+{
+ INIT_LIST_HEAD(to_buf_list(vb));
+ return 0;
+}
+
+static int rcar_vin_stop_streaming(struct vb2_queue *vq)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct list_head *buf_head, *tmp;
+
+ spin_lock_irq(&priv->lock);
+ list_for_each_safe(buf_head, tmp, &priv->capture)
+ list_del_init(buf_head);
+ spin_unlock_irq(&priv->lock);
+
+ return 0;
+}
+
+static struct vb2_ops rcar_vin_vb2_ops = {
+ .queue_setup = rcar_vin_videobuf_setup,
+ .buf_init = rcar_vin_videobuf_init,
+ .buf_cleanup = rcar_vin_videobuf_release,
+ .buf_queue = rcar_vin_videobuf_queue,
+ .stop_streaming = rcar_vin_stop_streaming,
+ .wait_prepare = soc_camera_unlock,
+ .wait_finish = soc_camera_lock,
+};
+
+static irqreturn_t rcar_vin_irq(int irq, void *data)
+{
+ struct rcar_vin_priv *priv = data;
+ u32 int_status;
+ bool can_run = false, hw_stopped;
+ int slot;
+ unsigned int handled = 0;
+
+ spin_lock(&priv->lock);
+
+ int_status = ioread32(priv->base + VNINTS_REG);
+ if (!int_status)
+ goto done;
+ /* ack interrupts */
+ iowrite32(int_status, priv->base + VNINTS_REG);
+ handled = 1;
+
+ /* nothing to do if capture status is 'STOPPED' */
+ if (priv->state == STOPPED)
+ goto done;
+
+ hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA);
+
+ if (!priv->request_to_stop) {
+ if (is_continuous_transfer(priv))
+ slot = (ioread32(priv->base + VNMS_REG) &
+ VNMS_FBS_MASK) >> VNMS_FBS_SHIFT;
+ else
+ slot = 0;
+
+ priv->queue_buf[slot]->v4l2_buf.field = priv->field;
+ priv->queue_buf[slot]->v4l2_buf.sequence = priv->sequence++;
+ do_gettimeofday(&priv->queue_buf[slot]->v4l2_buf.timestamp);
+ vb2_buffer_done(priv->queue_buf[slot], VB2_BUF_STATE_DONE);
+ priv->queue_buf[slot] = NULL;
+
+ if (priv->state != STOPPING)
+ can_run = rcar_vin_fill_hw_slot(priv);
+
+ if (hw_stopped || !can_run) {
+ priv->state = STOPPED;
+ } else if (is_continuous_transfer(priv) &&
+ list_empty(&priv->capture) &&
+ priv->state == RUNNING) {
+ /*
+ * The continuous capturing requires an explicit stop
+ * operation when there is no buffer to be set into
+ * the VnMBm registers.
+ */
+ rcar_vin_request_capture_stop(priv);
+ } else {
+ rcar_vin_capture(priv);
+ }
+
+ } else if (hw_stopped) {
+ priv->state = STOPPED;
+ priv->request_to_stop = false;
+ complete(&priv->capture_stop);
+ }
+
+done:
+ spin_unlock(&priv->lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+static int rcar_vin_add_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ int i;
+
+ for (i = 0; i < MAX_BUFFER_NUM; i++)
+ priv->queue_buf[i] = NULL;
+
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
+ dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n",
+ icd->devnum);
+
+ return 0;
+}
+
+static void rcar_vin_remove_device(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct vb2_buffer *vb;
+ int i;
+
+ /* disable capture, disable interrupts */
+ iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME,
+ priv->base + VNMC_REG);
+ iowrite32(0, priv->base + VNIE_REG);
+
+ priv->state = STOPPED;
+ priv->request_to_stop = false;
+
+ /* make sure active buffer is cancelled */
+ spin_lock_irq(&priv->lock);
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ vb = priv->queue_buf[i];
+ if (vb) {
+ list_del_init(to_buf_list(vb));
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ }
+ }
+ spin_unlock_irq(&priv->lock);
+
+ pm_runtime_put(ici->v4l2_dev.dev);
+
+ dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n",
+ icd->devnum);
+}
+
+/* Called with .host_lock held */
+static int rcar_vin_clock_start(struct soc_camera_host *ici)
+{
+ /* VIN does not have "mclk" */
+ return 0;
+}
+
+/* Called with .host_lock held */
+static void rcar_vin_clock_stop(struct soc_camera_host *ici)
+{
+ /* VIN does not have "mclk" */
+}
+
+/* rect is guaranteed to not exceed the scaled camera rectangle */
+static int rcar_vin_set_rect(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct rcar_vin_priv *priv = ici->priv;
+ unsigned int left_offset, top_offset;
+ unsigned char dsize = 0;
+ struct v4l2_rect *cam_subrect = &cam->subrect;
+
+ dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height, cam->vin_left, cam->vin_top);
+
+ left_offset = cam->vin_left;
+ top_offset = cam->vin_top;
+
+ if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_RGB32 &&
+ priv->chip == RCAR_E1)
+ dsize = 1;
+
+ dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n",
+ cam->width, cam->height, cam->vin_left, cam->vin_top);
+ dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n",
+ cam_subrect->width, cam_subrect->height,
+ cam_subrect->left, cam_subrect->top);
+
+ /* Set Start/End Pixel/Line Pre-Clip */
+ iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG);
+ iowrite32((left_offset + cam->width - 1) << dsize,
+ priv->base + VNEPPRC_REG);
+ switch (priv->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ iowrite32(top_offset / 2, priv->base + VNSLPRC_REG);
+ iowrite32((top_offset + cam->height) / 2 - 1,
+ priv->base + VNELPRC_REG);
+ break;
+ default:
+ iowrite32(top_offset, priv->base + VNSLPRC_REG);
+ iowrite32(top_offset + cam->height - 1,
+ priv->base + VNELPRC_REG);
+ break;
+ }
+
+ /* Set Start/End Pixel/Line Post-Clip */
+ iowrite32(0, priv->base + VNSPPOC_REG);
+ iowrite32(0, priv->base + VNSLPOC_REG);
+ iowrite32((cam_subrect->width - 1) << dsize, priv->base + VNEPPOC_REG);
+ switch (priv->field) {
+ case V4L2_FIELD_INTERLACED:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ iowrite32(cam_subrect->height / 2 - 1,
+ priv->base + VNELPOC_REG);
+ break;
+ default:
+ iowrite32(cam_subrect->height - 1, priv->base + VNELPOC_REG);
+ break;
+ }
+
+ iowrite32(ALIGN(cam->width, 0x10), priv->base + VNIS_REG);
+
+ return 0;
+}
+
+static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc)
+{
+ *vnmc = ioread32(priv->base + VNMC_REG);
+ /* module disable */
+ iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG);
+}
+
+static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc)
+{
+ unsigned long timeout = jiffies + 10 * HZ;
+
+ /*
+ * Wait until the end of the current frame. It can take a long time,
+ * but if it has been aborted by a MRST1 reset, it should exit sooner.
+ */
+ while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) &&
+ time_before(jiffies, timeout))
+ msleep(1);
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(priv->ici.v4l2_dev.dev,
+ "Timeout waiting for frame end! Interface problem?\n");
+ return;
+ }
+
+ iowrite32(vnmc, priv->base + VNMC_REG);
+}
+
+#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH)
+
+static int rcar_vin_set_bus_param(struct soc_camera_device *icd)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config cfg;
+ unsigned long common_flags;
+ u32 vnmc;
+ u32 val;
+ int ret;
+
+ capture_stop_preserve(priv, &vnmc);
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (!ret) {
+ common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS);
+ if (!common_flags) {
+ dev_warn(icd->parent,
+ "MBUS flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, VIN_MBUS_FLAGS);
+ return -EINVAL;
+ }
+ } else if (ret != -ENOIOCTLCMD) {
+ return ret;
+ } else {
+ common_flags = VIN_MBUS_FLAGS;
+ }
+
+ /* Make choises, based on platform preferences */
+ if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) {
+ if (priv->pdata->flags & RCAR_VIN_HSYNC_ACTIVE_LOW)
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW;
+ }
+
+ if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) &&
+ (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) {
+ if (priv->pdata->flags & RCAR_VIN_VSYNC_ACTIVE_LOW)
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH;
+ else
+ common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW;
+ }
+
+ cfg.flags = common_flags;
+ ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ val = priv->field == V4L2_FIELD_NONE ? VNDMR2_FTEV : 0;
+ if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW))
+ val |= VNDMR2_VPS;
+ if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW))
+ val |= VNDMR2_HPS;
+ iowrite32(val, priv->base + VNDMR2_REG);
+
+ ret = rcar_vin_set_rect(icd);
+ if (ret < 0)
+ return ret;
+
+ capture_restore(priv, vnmc);
+
+ return 0;
+}
+
+static int rcar_vin_try_bus_param(struct soc_camera_device *icd,
+ unsigned char buswidth)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_config cfg;
+ int ret;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg);
+ if (ret == -ENOIOCTLCMD)
+ return 0;
+ else if (ret)
+ return ret;
+
+ if (buswidth > 24)
+ return -EINVAL;
+
+ /* check is there common mbus flags */
+ ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS);
+ if (ret)
+ return 0;
+
+ dev_warn(icd->parent,
+ "MBUS flags incompatible: camera 0x%x, host 0x%x\n",
+ cfg.flags, VIN_MBUS_FLAGS);
+
+ return -EINVAL;
+}
+
+static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt)
+{
+ return fmt->packing == SOC_MBUS_PACKING_NONE ||
+ (fmt->bits_per_sample > 8 &&
+ fmt->packing == SOC_MBUS_PACKING_EXTEND16);
+}
+
+static const struct soc_mbus_pixelfmt rcar_vin_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .name = "NV16",
+ .bits_per_sample = 8,
+ .packing = SOC_MBUS_PACKING_2X8_PADHI,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .name = "UYVY",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .name = "RGB565",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+ .name = "ARGB1555",
+ .bits_per_sample = 16,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32,
+ .name = "RGB888",
+ .bits_per_sample = 32,
+ .packing = SOC_MBUS_PACKING_NONE,
+ .order = SOC_MBUS_ORDER_LE,
+ .layout = SOC_MBUS_LAYOUT_PACKED,
+ },
+};
+
+static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
+ struct soc_camera_format_xlate *xlate)
+{
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ int ret, k, n;
+ int formats = 0;
+ struct rcar_vin_cam *cam;
+ enum v4l2_mbus_pixelcode code;
+ const struct soc_mbus_pixelfmt *fmt;
+
+ ret = v4l2_subdev_call(sd, video, enum_mbus_fmt, idx, &code);
+ if (ret < 0)
+ return 0;
+
+ fmt = soc_mbus_get_fmtdesc(code);
+ if (!fmt) {
+ dev_warn(dev, "unsupported format code #%u: %d\n", idx, code);
+ return 0;
+ }
+
+ ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample);
+ if (ret < 0)
+ return 0;
+
+ if (!icd->host_priv) {
+ struct v4l2_mbus_framefmt mf;
+ struct v4l2_rect rect;
+ struct device *dev = icd->parent;
+ int shift;
+
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ /* Cache current client geometry */
+ ret = soc_camera_client_g_rect(sd, &rect);
+ if (ret == -ENOIOCTLCMD) {
+ /* Sensor driver doesn't support cropping */
+ rect.left = 0;
+ rect.top = 0;
+ rect.width = mf.width;
+ rect.height = mf.height;
+ } else if (ret < 0) {
+ return ret;
+ }
+
+ /*
+ * If sensor proposes too large format then try smaller ones:
+ * 1280x960, 640x480, 320x240
+ */
+ for (shift = 0; shift < 3; shift++) {
+ if (mf.width <= VIN_MAX_WIDTH &&
+ mf.height <= VIN_MAX_HEIGHT)
+ break;
+
+ mf.width = 1280 >> shift;
+ mf.height = 960 >> shift;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd),
+ video, s_mbus_fmt,
+ &mf);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (shift == 3) {
+ dev_err(dev,
+ "Failed to configure the client below %ux%x\n",
+ mf.width, mf.height);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "camera fmt %ux%u\n", mf.width, mf.height);
+
+ cam = kzalloc(sizeof(*cam), GFP_KERNEL);
+ if (!cam)
+ return -ENOMEM;
+ /*
+ * We are called with current camera crop,
+ * initialise subrect with it
+ */
+ cam->rect = rect;
+ cam->subrect = rect;
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->host_priv = cam;
+ } else {
+ cam = icd->host_priv;
+ }
+
+ /* Beginning of a pass */
+ if (!idx)
+ cam->extra_fmt = NULL;
+
+ switch (code) {
+ case V4L2_MBUS_FMT_YUYV8_1X16:
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ if (cam->extra_fmt)
+ break;
+
+ /* Add all our formats that can be generated by VIN */
+ cam->extra_fmt = rcar_vin_formats;
+
+ n = ARRAY_SIZE(rcar_vin_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++, xlate++) {
+ xlate->host_fmt = &rcar_vin_formats[k];
+ xlate->code = code;
+ dev_dbg(dev, "Providing format %s using code %d\n",
+ rcar_vin_formats[k].name, code);
+ }
+ break;
+ default:
+ if (!rcar_vin_packing_supported(fmt))
+ return 0;
+
+ dev_dbg(dev, "Providing format %s in pass-through mode\n",
+ fmt->name);
+ break;
+ }
+
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+ xlate->host_fmt = fmt;
+ xlate->code = code;
+ xlate++;
+ }
+
+ return formats;
+}
+
+static void rcar_vin_put_formats(struct soc_camera_device *icd)
+{
+ kfree(icd->host_priv);
+ icd->host_priv = NULL;
+}
+
+static int rcar_vin_set_crop(struct soc_camera_device *icd,
+ const struct v4l2_crop *a)
+{
+ struct v4l2_crop a_writable = *a;
+ const struct v4l2_rect *rect = &a_writable.c;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_crop cam_crop;
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct v4l2_rect *cam_rect = &cam_crop.c;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct device *dev = icd->parent;
+ struct v4l2_mbus_framefmt mf;
+ u32 vnmc;
+ int ret, i;
+
+ dev_dbg(dev, "S_CROP(%ux%u@%u:%u)\n", rect->width, rect->height,
+ rect->left, rect->top);
+
+ /* During camera cropping its output window can change too, stop VIN */
+ capture_stop_preserve(priv, &vnmc);
+ dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc);
+
+ /* Apply iterative camera S_CROP for new input window. */
+ ret = soc_camera_client_s_crop(sd, &a_writable, &cam_crop,
+ &cam->rect, &cam->subrect);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n",
+ cam_rect->width, cam_rect->height,
+ cam_rect->left, cam_rect->top);
+
+ /* On success cam_crop contains current camera crop */
+
+ /* Retrieve camera output window */
+ ret = v4l2_subdev_call(sd, video, g_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ if (mf.width > VIN_MAX_WIDTH || mf.height > VIN_MAX_HEIGHT)
+ return -EINVAL;
+
+ /* Cache camera output window */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ icd->user_width = cam->width;
+ icd->user_height = cam->height;
+
+ cam->vin_left = rect->left & ~1;
+ cam->vin_top = rect->top & ~1;
+
+ /* Use VIN cropping to crop to the new window. */
+ ret = rcar_vin_set_rect(icd);
+ if (ret < 0)
+ return ret;
+
+ cam->subrect = *rect;
+
+ dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n",
+ icd->user_width, icd->user_height,
+ cam->vin_left, cam->vin_top);
+
+ /* Restore capture */
+ for (i = 0; i < MAX_BUFFER_NUM; i++) {
+ if (priv->queue_buf[i] && priv->state == STOPPED) {
+ vnmc |= VNMC_ME;
+ break;
+ }
+ }
+ capture_restore(priv, vnmc);
+
+ /* Even if only camera cropping succeeded */
+ return ret;
+}
+
+static int rcar_vin_get_crop(struct soc_camera_device *icd,
+ struct v4l2_crop *a)
+{
+ struct rcar_vin_cam *cam = icd->host_priv;
+
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ a->c = cam->subrect;
+
+ return 0;
+}
+
+/* Similar to set_crop multistage iterative algorithm */
+static int rcar_vin_set_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct rcar_vin_priv *priv = ici->priv;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct rcar_vin_cam *cam = icd->host_priv;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_mbus_framefmt mf;
+ struct device *dev = icd->parent;
+ __u32 pixfmt = pix->pixelformat;
+ const struct soc_camera_format_xlate *xlate;
+ unsigned int vin_sub_width = 0, vin_sub_height = 0;
+ int ret;
+ bool can_scale;
+ enum v4l2_field field;
+ v4l2_std_id std;
+
+ dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n",
+ pixfmt, pix->width, pix->height);
+
+ switch (pix->field) {
+ default:
+ pix->field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ case V4L2_FIELD_INTERLACED_TB:
+ case V4L2_FIELD_INTERLACED_BT:
+ field = pix->field;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ /* Query for standard if not explicitly mentioned _TB/_BT */
+ ret = v4l2_subdev_call(sd, video, querystd, &std);
+ if (ret < 0)
+ std = V4L2_STD_625_50;
+
+ field = std & V4L2_STD_625_50 ? V4L2_FIELD_INTERLACED_TB :
+ V4L2_FIELD_INTERLACED_BT;
+ break;
+ }
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+ /* Calculate client output geometry */
+ 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;
+
+ switch (pixfmt) {
+ case V4L2_PIX_FMT_RGB32:
+ can_scale = priv->chip != RCAR_E1;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB555X:
+ can_scale = true;
+ break;
+ default:
+ can_scale = false;
+ break;
+ }
+
+ dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height);
+
+ ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect,
+ &mf, &vin_sub_width, &vin_sub_height,
+ can_scale, 12);
+
+ /* Done with the camera. Now see if we can improve the result */
+ dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n",
+ ret, mf.width, mf.height, pix->width, pix->height);
+
+ if (ret == -ENOIOCTLCMD)
+ dev_dbg(dev, "Sensor doesn't support scaling\n");
+ else if (ret < 0)
+ return ret;
+
+ if (mf.code != xlate->code)
+ return -EINVAL;
+
+ /* Prepare VIN crop */
+ cam->width = mf.width;
+ cam->height = mf.height;
+
+ /* Use VIN scaling to scale to the requested user window. */
+
+ /* We cannot scale up */
+ if (pix->width > vin_sub_width)
+ vin_sub_width = pix->width;
+
+ if (pix->height > vin_sub_height)
+ vin_sub_height = pix->height;
+
+ pix->colorspace = mf.colorspace;
+
+ if (!can_scale) {
+ pix->width = vin_sub_width;
+ pix->height = vin_sub_height;
+ }
+
+ /*
+ * We have calculated CFLCR, the actual configuration will be performed
+ * in rcar_vin_set_bus_param()
+ */
+
+ dev_dbg(dev, "W: %u : %u, H: %u : %u\n",
+ vin_sub_width, pix->width, vin_sub_height, pix->height);
+
+ icd->current_fmt = xlate;
+
+ priv->field = field;
+
+ return 0;
+}
+
+static int rcar_vin_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+{
+ const struct soc_camera_format_xlate *xlate;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
+ struct v4l2_mbus_framefmt mf;
+ __u32 pixfmt = pix->pixelformat;
+ int width, height;
+ int ret;
+
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ xlate = icd->current_fmt;
+ dev_dbg(icd->parent, "Format %x not found, keeping %x\n",
+ pixfmt, xlate->host_fmt->fourcc);
+ pixfmt = xlate->host_fmt->fourcc;
+ pix->pixelformat = pixfmt;
+ pix->colorspace = icd->colorspace;
+ }
+
+ /* FIXME: calculate using depth and bus width */
+ v4l_bound_align_image(&pix->width, 2, VIN_MAX_WIDTH, 1,
+ &pix->height, 4, VIN_MAX_HEIGHT, 2, 0);
+
+ width = pix->width;
+ height = pix->height;
+
+ /* let soc-camera calculate these values */
+ pix->bytesperline = 0;
+ pix->sizeimage = 0;
+
+ /* limit to sensor capabilities */
+ mf.width = pix->width;
+ mf.height = pix->height;
+ mf.field = pix->field;
+ mf.code = xlate->code;
+ mf.colorspace = pix->colorspace;
+
+ ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd),
+ video, try_mbus_fmt, &mf);
+ if (ret < 0)
+ return ret;
+
+ pix->width = mf.width;
+ pix->height = mf.height;
+ pix->field = mf.field;
+ pix->colorspace = mf.colorspace;
+
+ if (pixfmt == V4L2_PIX_FMT_NV16) {
+ /* FIXME: check against rect_max after converting soc-camera */
+ /* We can scale precisely, need a bigger image from camera */
+ if (pix->width < width || pix->height < height) {
+ /*
+ * We presume, the sensor behaves sanely, i.e. if
+ * requested a bigger rectangle, it will not return a
+ * smaller one.
+ */
+ mf.width = VIN_MAX_WIDTH;
+ mf.height = VIN_MAX_HEIGHT;
+ ret = v4l2_device_call_until_err(sd->v4l2_dev,
+ soc_camera_grp_id(icd),
+ video, try_mbus_fmt,
+ &mf);
+ if (ret < 0) {
+ dev_err(icd->parent,
+ "client try_fmt() = %d\n", ret);
+ return ret;
+ }
+ }
+ /* We will scale exactly */
+ if (mf.width > width)
+ pix->width = width;
+ if (mf.height > height)
+ pix->height = height;
+ }
+
+ return ret;
+}
+
+static unsigned int rcar_vin_poll(struct file *file, poll_table *pt)
+{
+ struct soc_camera_device *icd = file->private_data;
+
+ return vb2_poll(&icd->vb2_vidq, file, pt);
+}
+
+static int rcar_vin_querycap(struct soc_camera_host *ici,
+ struct v4l2_capability *cap)
+{
+ strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+}
+
+static int rcar_vin_init_videobuf2(struct vb2_queue *vq,
+ struct soc_camera_device *icd)
+{
+ vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vq->io_modes = VB2_MMAP | VB2_USERPTR;
+ vq->drv_priv = icd;
+ vq->ops = &rcar_vin_vb2_ops;
+ vq->mem_ops = &vb2_dma_contig_memops;
+ vq->buf_struct_size = sizeof(struct rcar_vin_buffer);
+ vq->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ return vb2_queue_init(vq);
+}
+
+static struct soc_camera_host_ops rcar_vin_host_ops = {
+ .owner = THIS_MODULE,
+ .add = rcar_vin_add_device,
+ .remove = rcar_vin_remove_device,
+ .clock_start = rcar_vin_clock_start,
+ .clock_stop = rcar_vin_clock_stop,
+ .get_formats = rcar_vin_get_formats,
+ .put_formats = rcar_vin_put_formats,
+ .get_crop = rcar_vin_get_crop,
+ .set_crop = rcar_vin_set_crop,
+ .try_fmt = rcar_vin_try_fmt,
+ .set_fmt = rcar_vin_set_fmt,
+ .poll = rcar_vin_poll,
+ .querycap = rcar_vin_querycap,
+ .set_bus_param = rcar_vin_set_bus_param,
+ .init_videobuf2 = rcar_vin_init_videobuf2,
+};
+
+static struct platform_device_id rcar_vin_id_table[] = {
+ { "r8a7779-vin", RCAR_H1 },
+ { "r8a7778-vin", RCAR_M1 },
+ { "uPD35004-vin", RCAR_E1 },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, rcar_vin_id_table);
+
+static int rcar_vin_probe(struct platform_device *pdev)
+{
+ struct rcar_vin_priv *priv;
+ struct resource *mem;
+ struct rcar_vin_platform_data *pdata;
+ int irq, ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata || !pdata->flags) {
+ dev_err(&pdev->dev, "platform data not set\n");
+ return -EINVAL;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem == NULL)
+ return -EINVAL;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv),
+ GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED,
+ dev_name(&pdev->dev), priv);
+ if (ret)
+ return ret;
+
+ priv->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(priv->alloc_ctx))
+ return PTR_ERR(priv->alloc_ctx);
+
+ priv->ici.priv = priv;
+ priv->ici.v4l2_dev.dev = &pdev->dev;
+ priv->ici.nr = pdev->id;
+ priv->ici.drv_name = dev_name(&pdev->dev);
+ priv->ici.ops = &rcar_vin_host_ops;
+
+ priv->pdata = pdata;
+ priv->chip = pdev->id_entry->driver_data;
+ spin_lock_init(&priv->lock);
+ INIT_LIST_HEAD(&priv->capture);
+
+ priv->state = STOPPED;
+
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = soc_camera_host_register(&priv->ici);
+ if (ret)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ pm_runtime_disable(&pdev->dev);
+ vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
+
+ return ret;
+}
+
+static int rcar_vin_remove(struct platform_device *pdev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev);
+ struct rcar_vin_priv *priv = container_of(soc_host,
+ struct rcar_vin_priv, ici);
+
+ soc_camera_host_unregister(soc_host);
+ pm_runtime_disable(&pdev->dev);
+ vb2_dma_contig_cleanup_ctx(priv->alloc_ctx);
+
+ return 0;
+}
+
+static struct platform_driver rcar_vin_driver = {
+ .probe = rcar_vin_probe,
+ .remove = rcar_vin_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .id_table = rcar_vin_id_table,
+};
+
+module_platform_driver(rcar_vin_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rcar_vin");
+MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver");
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 f2de0066089a..8df22f779175 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -610,13 +610,12 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd)
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);
+ sh_mobile_ceu_soft_reset(pcdev);
return 0;
}
@@ -1837,9 +1836,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
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,
+ __func__, i, (*asd)->match_type);
+ if ((*asd)->match_type == V4L2_ASYNC_MATCH_DEVNAME &&
+ !strncmp(name, (*asd)->match.device_name.name,
sizeof(name) - 1)) {
pcdev->csi2_asd = *asd;
break;
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index 2dd0e5272941..387a232d95a4 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -136,7 +136,7 @@ EXPORT_SYMBOL(soc_camera_power_off);
int soc_camera_power_init(struct device *dev, struct soc_camera_subdev_desc *ssdd)
{
-
+ /* Should not have any effect in synchronous case */
return devm_regulator_bulk_get(dev, ssdd->num_regulators,
ssdd->regulators);
}
@@ -1311,6 +1311,7 @@ eusrfmt:
static int soc_camera_i2c_init(struct soc_camera_device *icd,
struct soc_camera_desc *sdesc)
{
+ struct soc_camera_subdev_desc *ssdd;
struct i2c_client *client;
struct soc_camera_host *ici;
struct soc_camera_host_desc *shd = &sdesc->host_desc;
@@ -1333,7 +1334,21 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
return -ENODEV;
}
- shd->board_info->platform_data = &sdesc->subdev_desc;
+ ssdd = kzalloc(sizeof(*ssdd), GFP_KERNEL);
+ if (!ssdd) {
+ ret = -ENOMEM;
+ goto ealloc;
+ }
+
+ memcpy(ssdd, &sdesc->subdev_desc, sizeof(*ssdd));
+ /*
+ * In synchronous case we request regulators ourselves in
+ * soc_camera_pdrv_probe(), make sure the subdevice driver doesn't try
+ * to allocate them again.
+ */
+ ssdd->num_regulators = 0;
+ ssdd->regulators = NULL;
+ shd->board_info->platform_data = ssdd;
snprintf(clk_name, sizeof(clk_name), "%d-%04x",
shd->i2c_adapter_id, shd->board_info->addr);
@@ -1359,8 +1374,10 @@ static int soc_camera_i2c_init(struct soc_camera_device *icd,
return 0;
ei2cnd:
v4l2_clk_unregister(icd->clk);
-eclkreg:
icd->clk = NULL;
+eclkreg:
+ kfree(ssdd);
+ealloc:
i2c_put_adapter(adap);
return ret;
}
@@ -1370,15 +1387,18 @@ 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;
+ struct soc_camera_subdev_desc *ssdd;
icd->control = NULL;
if (icd->sasc)
return;
adap = client->adapter;
+ ssdd = client->dev.platform_data;
v4l2_device_unregister_subdev(i2c_get_clientdata(client));
i2c_unregister_device(client);
i2c_put_adapter(adap);
+ kfree(ssdd);
v4l2_clk_unregister(icd->clk);
icd->clk = NULL;
}
@@ -1466,7 +1486,8 @@ static int scan_async_group(struct soc_camera_host *ici,
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;
+ unsigned int i;
+ int ret;
/* First look for a sensor */
for (i = 0; i < size; i++) {
@@ -1475,7 +1496,7 @@ static int scan_async_group(struct soc_camera_host *ici,
break;
}
- if (i == size || asd[i]->bus_type != V4L2_ASYNC_BUS_I2C) {
+ if (i >= size || asd[i]->match_type != V4L2_ASYNC_MATCH_I2C) {
/* All useless */
dev_err(ici->v4l2_dev.dev, "No I2C data source found!\n");
return -ENODEV;
@@ -1501,7 +1522,7 @@ static int scan_async_group(struct soc_camera_host *ici,
return -ENOMEM;
}
- sasc->notifier.subdev = asd;
+ sasc->notifier.subdevs = asd;
sasc->notifier.num_subdevs = size;
sasc->notifier.bound = soc_camera_async_bound;
sasc->notifier.unbind = soc_camera_async_unbind;
@@ -1994,9 +2015,10 @@ static int soc_camera_pdrv_probe(struct platform_device *pdev)
/*
* 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.
+ * regulator allocation is a dummy. They are actually requested by the
+ * subdevice driver, using soc_camera_power_init(). 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);
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile
new file mode 100644
index 000000000000..4da226169e15
--- /dev/null
+++ b/drivers/media/platform/vsp1/Makefile
@@ -0,0 +1,5 @@
+vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o
+vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
+vsp1-y += vsp1_lif.o vsp1_uds.o
+
+obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h
new file mode 100644
index 000000000000..d6c6ecd039ff
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1.h
@@ -0,0 +1,74 @@
+/*
+ * vsp1.h -- R-Car VSP1 Driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_H__
+#define __VSP1_H__
+
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/vsp1.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_regs.h"
+
+struct clk;
+struct device;
+
+struct vsp1_platform_data;
+struct vsp1_lif;
+struct vsp1_rwpf;
+struct vsp1_uds;
+
+#define VPS1_MAX_RPF 5
+#define VPS1_MAX_UDS 3
+#define VPS1_MAX_WPF 4
+
+struct vsp1_device {
+ struct device *dev;
+ struct vsp1_platform_data *pdata;
+
+ void __iomem *mmio;
+ struct clk *clock;
+ struct clk *rt_clock;
+
+ struct mutex lock;
+ int ref_count;
+
+ struct vsp1_lif *lif;
+ struct vsp1_rwpf *rpf[VPS1_MAX_RPF];
+ struct vsp1_uds *uds[VPS1_MAX_UDS];
+ struct vsp1_rwpf *wpf[VPS1_MAX_WPF];
+
+ struct list_head entities;
+
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+};
+
+struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1);
+void vsp1_device_put(struct vsp1_device *vsp1);
+
+static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
+{
+ return ioread32(vsp1->mmio + reg);
+}
+
+static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data)
+{
+ iowrite32(data, vsp1->mmio + reg);
+}
+
+#endif /* __VSP1_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
new file mode 100644
index 000000000000..1c9e771aa15c
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -0,0 +1,527 @@
+/*
+ * vsp1_drv.c -- R-Car VSP1 Driver
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
+#include "vsp1.h"
+#include "vsp1_lif.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_uds.h"
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t vsp1_irq_handler(int irq, void *data)
+{
+ u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
+ struct vsp1_device *vsp1 = data;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned int i;
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+ u32 status;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
+ vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
+
+ if (status & VI6_WFP_IRQ_STA_FRE) {
+ vsp1_pipeline_frame_end(pipe);
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Entities
+ */
+
+/*
+ * vsp1_create_links - Create links from all sources to the given sink
+ *
+ * This function creates media links from all valid sources to the given sink
+ * pad. Links that would be invalid according to the VSP1 hardware capabilities
+ * are skipped. Those include all links
+ *
+ * - from a UDS to a UDS (UDS entities can't be chained)
+ * - from an entity to itself (no loops are allowed)
+ */
+static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
+{
+ struct media_entity *entity = &sink->subdev.entity;
+ struct vsp1_entity *source;
+ unsigned int pad;
+ int ret;
+
+ list_for_each_entry(source, &vsp1->entities, list_dev) {
+ u32 flags;
+
+ if (source->type == sink->type)
+ continue;
+
+ if (source->type == VSP1_ENTITY_LIF ||
+ source->type == VSP1_ENTITY_WPF)
+ continue;
+
+ flags = source->type == VSP1_ENTITY_RPF &&
+ sink->type == VSP1_ENTITY_WPF &&
+ source->index == sink->index
+ ? MEDIA_LNK_FL_ENABLED : 0;
+
+ for (pad = 0; pad < entity->num_pads; ++pad) {
+ if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
+ continue;
+
+ ret = media_entity_create_link(&source->subdev.entity,
+ source->source_pad,
+ entity, pad, flags);
+ if (ret < 0)
+ return ret;
+
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ source->sink = entity;
+ }
+ }
+
+ return 0;
+}
+
+static void vsp1_destroy_entities(struct vsp1_device *vsp1)
+{
+ struct vsp1_entity *entity;
+ struct vsp1_entity *next;
+
+ list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) {
+ list_del(&entity->list_dev);
+ vsp1_entity_destroy(entity);
+ }
+
+ v4l2_device_unregister(&vsp1->v4l2_dev);
+ media_device_unregister(&vsp1->media_dev);
+}
+
+static int vsp1_create_entities(struct vsp1_device *vsp1)
+{
+ struct media_device *mdev = &vsp1->media_dev;
+ struct v4l2_device *vdev = &vsp1->v4l2_dev;
+ struct vsp1_entity *entity;
+ unsigned int i;
+ int ret;
+
+ mdev->dev = vsp1->dev;
+ strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
+ snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
+ dev_name(mdev->dev));
+ ret = media_device_register(mdev);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "media device registration failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ vdev->mdev = mdev;
+ ret = v4l2_device_register(vsp1->dev, vdev);
+ if (ret < 0) {
+ dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
+ ret);
+ goto done;
+ }
+
+ /* Instantiate all the entities. */
+ if (vsp1->pdata->features & VSP1_HAS_LIF) {
+ vsp1->lif = vsp1_lif_create(vsp1);
+ if (IS_ERR(vsp1->lif)) {
+ ret = PTR_ERR(vsp1->lif);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->rpf_count; ++i) {
+ struct vsp1_rwpf *rpf;
+
+ rpf = vsp1_rpf_create(vsp1, i);
+ if (IS_ERR(rpf)) {
+ ret = PTR_ERR(rpf);
+ goto done;
+ }
+
+ vsp1->rpf[i] = rpf;
+ list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->uds_count; ++i) {
+ struct vsp1_uds *uds;
+
+ uds = vsp1_uds_create(vsp1, i);
+ if (IS_ERR(uds)) {
+ ret = PTR_ERR(uds);
+ goto done;
+ }
+
+ vsp1->uds[i] = uds;
+ list_add_tail(&uds->entity.list_dev, &vsp1->entities);
+ }
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ struct vsp1_rwpf *wpf;
+
+ wpf = vsp1_wpf_create(vsp1, i);
+ if (IS_ERR(wpf)) {
+ ret = PTR_ERR(wpf);
+ goto done;
+ }
+
+ vsp1->wpf[i] = wpf;
+ list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
+ }
+
+ /* Create links. */
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ if (entity->type == VSP1_ENTITY_LIF ||
+ entity->type == VSP1_ENTITY_RPF)
+ continue;
+
+ ret = vsp1_create_links(vsp1, entity);
+ if (ret < 0)
+ goto done;
+ }
+
+ if (vsp1->pdata->features & VSP1_HAS_LIF) {
+ ret = media_entity_create_link(
+ &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
+ &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Register all subdevs. */
+ list_for_each_entry(entity, &vsp1->entities, list_dev) {
+ ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
+ &entity->subdev);
+ if (ret < 0)
+ goto done;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
+
+done:
+ if (ret < 0)
+ vsp1_destroy_entities(vsp1);
+
+ return ret;
+}
+
+static int vsp1_device_init(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+ u32 status;
+
+ /* Reset any channel that might be running. */
+ status = vsp1_read(vsp1, VI6_STATUS);
+
+ for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
+ unsigned int timeout;
+
+ if (!(status & VI6_STATUS_SYS_ACT(i)))
+ continue;
+
+ vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i));
+ for (timeout = 10; timeout > 0; --timeout) {
+ status = vsp1_read(vsp1, VI6_STATUS);
+ if (!(status & VI6_STATUS_SYS_ACT(i)))
+ break;
+
+ usleep_range(1000, 2000);
+ }
+
+ if (!timeout) {
+ dev_err(vsp1->dev, "failed to reset wpf.%u\n", i);
+ return -ETIMEDOUT;
+ }
+ }
+
+ vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
+ (8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
+
+ for (i = 0; i < vsp1->pdata->rpf_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
+ for (i = 0; i < vsp1->pdata->uds_count; ++i)
+ vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
+
+ vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
+ vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
+
+ vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
+ (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
+ vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
+ (VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
+
+ return 0;
+}
+
+static int vsp1_clocks_enable(struct vsp1_device *vsp1)
+{
+ int ret;
+
+ ret = clk_prepare_enable(vsp1->clock);
+ if (ret < 0)
+ return ret;
+
+ if (IS_ERR(vsp1->rt_clock))
+ return 0;
+
+ ret = clk_prepare_enable(vsp1->rt_clock);
+ if (ret < 0) {
+ clk_disable_unprepare(vsp1->clock);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void vsp1_clocks_disable(struct vsp1_device *vsp1)
+{
+ if (!IS_ERR(vsp1->rt_clock))
+ clk_disable_unprepare(vsp1->rt_clock);
+ clk_disable_unprepare(vsp1->clock);
+}
+
+/*
+ * vsp1_device_get - Acquire the VSP1 device
+ *
+ * Increment the VSP1 reference count and initialize the device if the first
+ * reference is taken.
+ *
+ * Return a pointer to the VSP1 device or NULL if an error occured.
+ */
+struct vsp1_device *vsp1_device_get(struct vsp1_device *vsp1)
+{
+ struct vsp1_device *__vsp1 = vsp1;
+ int ret;
+
+ mutex_lock(&vsp1->lock);
+ if (vsp1->ref_count > 0)
+ goto done;
+
+ ret = vsp1_clocks_enable(vsp1);
+ if (ret < 0) {
+ __vsp1 = NULL;
+ goto done;
+ }
+
+ ret = vsp1_device_init(vsp1);
+ if (ret < 0) {
+ vsp1_clocks_disable(vsp1);
+ __vsp1 = NULL;
+ goto done;
+ }
+
+done:
+ if (__vsp1)
+ vsp1->ref_count++;
+
+ mutex_unlock(&vsp1->lock);
+ return __vsp1;
+}
+
+/*
+ * vsp1_device_put - Release the VSP1 device
+ *
+ * Decrement the VSP1 reference count and cleanup the device if the last
+ * reference is released.
+ */
+void vsp1_device_put(struct vsp1_device *vsp1)
+{
+ mutex_lock(&vsp1->lock);
+
+ if (--vsp1->ref_count == 0)
+ vsp1_clocks_disable(vsp1);
+
+ mutex_unlock(&vsp1->lock);
+}
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+#ifdef CONFIG_PM_SLEEP
+static int vsp1_pm_suspend(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&vsp1->lock));
+
+ if (vsp1->ref_count == 0)
+ return 0;
+
+ vsp1_clocks_disable(vsp1);
+ return 0;
+}
+
+static int vsp1_pm_resume(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ WARN_ON(mutex_is_locked(&vsp1->lock));
+
+ if (vsp1->ref_count)
+ return 0;
+
+ return vsp1_clocks_enable(vsp1);
+}
+#endif
+
+static const struct dev_pm_ops vsp1_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Platform Driver
+ */
+
+static struct vsp1_platform_data *
+vsp1_get_platform_data(struct platform_device *pdev)
+{
+ struct vsp1_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return NULL;
+ }
+
+ if (pdata->rpf_count <= 0 || pdata->rpf_count > VPS1_MAX_RPF) {
+ dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
+ pdata->rpf_count);
+ return NULL;
+ }
+
+ if (pdata->uds_count <= 0 || pdata->uds_count > VPS1_MAX_UDS) {
+ dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
+ pdata->uds_count);
+ return NULL;
+ }
+
+ if (pdata->wpf_count <= 0 || pdata->wpf_count > VPS1_MAX_WPF) {
+ dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
+ pdata->wpf_count);
+ return NULL;
+ }
+
+ return pdata;
+}
+
+static int vsp1_probe(struct platform_device *pdev)
+{
+ struct vsp1_device *vsp1;
+ struct resource *irq;
+ struct resource *io;
+ int ret;
+
+ vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
+ if (vsp1 == NULL)
+ return -ENOMEM;
+
+ vsp1->dev = &pdev->dev;
+ mutex_init(&vsp1->lock);
+ INIT_LIST_HEAD(&vsp1->entities);
+
+ vsp1->pdata = vsp1_get_platform_data(pdev);
+ if (vsp1->pdata == NULL)
+ return -ENODEV;
+
+ /* I/O, IRQ and clock resources */
+ io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
+ if (IS_ERR(vsp1->mmio))
+ return PTR_ERR(vsp1->mmio);
+
+ vsp1->clock = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(vsp1->clock)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ return PTR_ERR(vsp1->clock);
+ }
+
+ /* The RT clock is optional */
+ vsp1->rt_clock = devm_clk_get(&pdev->dev, "rt");
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "missing IRQ\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), vsp1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ /* Instanciate entities */
+ ret = vsp1_create_entities(vsp1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to create entities\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, vsp1);
+
+ return 0;
+}
+
+static int vsp1_remove(struct platform_device *pdev)
+{
+ struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
+
+ vsp1_destroy_entities(vsp1);
+
+ return 0;
+}
+
+static struct platform_driver vsp1_platform_driver = {
+ .probe = vsp1_probe,
+ .remove = vsp1_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vsp1",
+ .pm = &vsp1_pm_ops,
+ },
+};
+
+module_platform_driver(vsp1_platform_driver);
+
+MODULE_ALIAS("vsp1");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_DESCRIPTION("Renesas VSP1 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
new file mode 100644
index 000000000000..9028f9d524f4
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -0,0 +1,181 @@
+/*
+ * vsp1_entity.c -- R-Car VSP1 Base Entity
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_entity.h"
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+struct v4l2_mbus_framefmt *
+vsp1_entity_get_pad_format(struct vsp1_entity *entity,
+ struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &entity->formats[pad];
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * vsp1_entity_init_formats - Initialize formats on all pads
+ * @subdev: V4L2 subdevice
+ * @fh: V4L2 subdev file handle
+ *
+ * Initialize all pad formats with default values. If fh is not NULL, try
+ * formats are initialized on the file handle. Otherwise active formats are
+ * initialized on the device.
+ */
+void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format;
+ unsigned int pad;
+
+ for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
+ memset(&format, 0, sizeof(format));
+
+ format.pad = pad;
+ format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
+ : V4L2_SUBDEV_FORMAT_ACTIVE;
+
+ v4l2_subdev_call(subdev, pad, set_fmt, fh, &format);
+ }
+}
+
+static int vsp1_entity_open(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh)
+{
+ vsp1_entity_init_formats(subdev, fh);
+
+ return 0;
+}
+
+const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
+ .open = vsp1_entity_open,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static int vsp1_entity_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct vsp1_entity *source;
+
+ if (!(local->flags & MEDIA_PAD_FL_SOURCE))
+ return 0;
+
+ source = container_of(local->entity, struct vsp1_entity, subdev.entity);
+
+ if (!source->route)
+ return 0;
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (source->sink)
+ return -EBUSY;
+ source->sink = remote->entity;
+ } else {
+ source->sink = NULL;
+ }
+
+ return 0;
+}
+
+const struct media_entity_operations vsp1_media_ops = {
+ .link_setup = vsp1_entity_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
+ unsigned int num_pads)
+{
+ static const struct {
+ unsigned int id;
+ unsigned int reg;
+ } routes[] = {
+ { VI6_DPR_NODE_LIF, 0 },
+ { VI6_DPR_NODE_RPF(0), VI6_DPR_RPF_ROUTE(0) },
+ { VI6_DPR_NODE_RPF(1), VI6_DPR_RPF_ROUTE(1) },
+ { VI6_DPR_NODE_RPF(2), VI6_DPR_RPF_ROUTE(2) },
+ { VI6_DPR_NODE_RPF(3), VI6_DPR_RPF_ROUTE(3) },
+ { VI6_DPR_NODE_RPF(4), VI6_DPR_RPF_ROUTE(4) },
+ { VI6_DPR_NODE_UDS(0), VI6_DPR_UDS_ROUTE(0) },
+ { VI6_DPR_NODE_UDS(1), VI6_DPR_UDS_ROUTE(1) },
+ { VI6_DPR_NODE_UDS(2), VI6_DPR_UDS_ROUTE(2) },
+ { VI6_DPR_NODE_WPF(0), 0 },
+ { VI6_DPR_NODE_WPF(1), 0 },
+ { VI6_DPR_NODE_WPF(2), 0 },
+ { VI6_DPR_NODE_WPF(3), 0 },
+ };
+
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(routes); ++i) {
+ if (routes[i].id == entity->id) {
+ entity->route = routes[i].reg;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(routes))
+ return -EINVAL;
+
+ entity->vsp1 = vsp1;
+ entity->source_pad = num_pads - 1;
+
+ /* Allocate formats and pads. */
+ entity->formats = devm_kzalloc(vsp1->dev,
+ num_pads * sizeof(*entity->formats),
+ GFP_KERNEL);
+ if (entity->formats == NULL)
+ return -ENOMEM;
+
+ entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
+ GFP_KERNEL);
+ if (entity->pads == NULL)
+ return -ENOMEM;
+
+ /* Initialize pads. */
+ for (i = 0; i < num_pads - 1; ++i)
+ entity->pads[i].flags = MEDIA_PAD_FL_SINK;
+
+ entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
+
+ /* Initialize the media entity. */
+ return media_entity_init(&entity->subdev.entity, num_pads,
+ entity->pads, 0);
+}
+
+void vsp1_entity_destroy(struct vsp1_entity *entity)
+{
+ media_entity_cleanup(&entity->subdev.entity);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
new file mode 100644
index 000000000000..c4feab2cbb81
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -0,0 +1,68 @@
+/*
+ * vsp1_entity.h -- R-Car VSP1 Base Entity
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_ENTITY_H__
+#define __VSP1_ENTITY_H__
+
+#include <linux/list.h>
+
+#include <media/v4l2-subdev.h>
+
+struct vsp1_device;
+
+enum vsp1_entity_type {
+ VSP1_ENTITY_LIF,
+ VSP1_ENTITY_RPF,
+ VSP1_ENTITY_UDS,
+ VSP1_ENTITY_WPF,
+};
+
+struct vsp1_entity {
+ struct vsp1_device *vsp1;
+
+ enum vsp1_entity_type type;
+ unsigned int index;
+ unsigned int id;
+ unsigned int route;
+
+ struct list_head list_dev;
+ struct list_head list_pipe;
+
+ struct media_pad *pads;
+ unsigned int source_pad;
+
+ struct media_entity *sink;
+
+ struct v4l2_subdev subdev;
+ struct v4l2_mbus_framefmt *formats;
+};
+
+static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_entity, subdev);
+}
+
+int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
+ unsigned int num_pads);
+void vsp1_entity_destroy(struct vsp1_entity *entity);
+
+extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
+extern const struct media_entity_operations vsp1_media_ops;
+
+struct v4l2_mbus_framefmt *
+vsp1_entity_get_pad_format(struct vsp1_entity *entity,
+ struct v4l2_subdev_fh *fh,
+ unsigned int pad, u32 which);
+void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh);
+
+#endif /* __VSP1_ENTITY_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/vsp1/vsp1_lif.c
new file mode 100644
index 000000000000..74a32e69ef10
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lif.c
@@ -0,0 +1,238 @@
+/*
+ * vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_lif.h"
+
+#define LIF_MIN_SIZE 2U
+#define LIF_MAX_SIZE 2048U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg)
+{
+ return vsp1_read(lif->entity.vsp1, reg);
+}
+
+static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
+{
+ vsp1_write(lif->entity.vsp1, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int lif_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_lif *lif = to_lif(subdev);
+ unsigned int hbth = 1300;
+ unsigned int obth = 400;
+ unsigned int lbth = 200;
+
+ if (!enable) {
+ vsp1_lif_write(lif, VI6_LIF_CTRL, 0);
+ return 0;
+ }
+
+ format = &lif->entity.formats[LIF_PAD_SOURCE];
+
+ obth = min(obth, (format->width + 1) / 2 * format->height - 4);
+
+ vsp1_lif_write(lif, VI6_LIF_CSBTH,
+ (hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
+ (lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
+
+ vsp1_lif_write(lif, VI6_LIF_CTRL,
+ (obth << VI6_LIF_CTRL_OBTH_SHIFT) |
+ (format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
+ VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->pad == LIF_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ struct v4l2_mbus_framefmt *format;
+
+ /* The LIF can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int lif_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == LIF_PAD_SINK) {
+ fse->min_width = LIF_MIN_SIZE;
+ fse->max_width = LIF_MAX_SIZE;
+ fse->min_height = LIF_MIN_SIZE;
+ fse->max_height = LIF_MAX_SIZE;
+ } else {
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lif *lif = to_lif(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_lif *lif = to_lif(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == LIF_PAD_SOURCE) {
+ /* The LIF source format is always identical to its sink
+ * format.
+ */
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->code = fmt->format.code;
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ LIF_MIN_SIZE, LIF_MAX_SIZE);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ LIF_MIN_SIZE, LIF_MAX_SIZE);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops lif_video_ops = {
+ .s_stream = lif_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops lif_pad_ops = {
+ .enum_mbus_code = lif_enum_mbus_code,
+ .enum_frame_size = lif_enum_frame_size,
+ .get_fmt = lif_get_format,
+ .set_fmt = lif_set_format,
+};
+
+static struct v4l2_subdev_ops lif_ops = {
+ .video = &lif_video_ops,
+ .pad = &lif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_lif *lif;
+ int ret;
+
+ lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL);
+ if (lif == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ lif->entity.type = VSP1_ENTITY_LIF;
+ lif->entity.id = VI6_DPR_NODE_LIF;
+
+ ret = vsp1_entity_init(vsp1, &lif->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &lif->entity.subdev;
+ v4l2_subdev_init(subdev, &lif_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s lif",
+ dev_name(vsp1->dev));
+ v4l2_set_subdevdata(subdev, lif);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return lif;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/vsp1/vsp1_lif.h
new file mode 100644
index 000000000000..89b93af56fdc
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_lif.h
@@ -0,0 +1,37 @@
+/*
+ * vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_LIF_H__
+#define __VSP1_LIF_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define LIF_PAD_SINK 0
+#define LIF_PAD_SOURCE 1
+
+struct vsp1_lif {
+ struct vsp1_entity entity;
+};
+
+static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_lif, entity.subdev);
+}
+
+struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_LIF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
new file mode 100644
index 000000000000..1d3304f1365b
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -0,0 +1,581 @@
+/*
+ * vsp1_regs.h -- R-Car VSP1 Registers Definitions
+ *
+ * Copyright (C) 2013 Renesas Electronics Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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.
+ */
+
+#ifndef __VSP1_REGS_H__
+#define __VSP1_REGS_H__
+
+/* -----------------------------------------------------------------------------
+ * General Control Registers
+ */
+
+#define VI6_CMD(n) (0x0000 + (n) * 4)
+#define VI6_CMD_STRCMD (1 << 0)
+
+#define VI6_CLK_DCSWT 0x0018
+#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8)
+#define VI6_CLK_DCSWT_CSTPW_SHIFT 8
+#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0)
+#define VI6_CLK_DCSWT_CSTRW_SHIFT 0
+
+#define VI6_SRESET 0x0028
+#define VI6_SRESET_SRTS(n) (1 << (n))
+
+#define VI6_STATUS 0x0038
+#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
+
+#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
+#define VI6_WFP_IRQ_ENB_DFEE (1 << 1)
+#define VI6_WFP_IRQ_ENB_FREE (1 << 0)
+
+#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12)
+#define VI6_WFP_IRQ_STA_DFE (1 << 1)
+#define VI6_WFP_IRQ_STA_FRE (1 << 0)
+
+#define VI6_DISP_IRQ_ENB 0x0078
+#define VI6_DISP_IRQ_ENB_DSTE (1 << 8)
+#define VI6_DISP_IRQ_ENB_MAEE (1 << 5)
+#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4))
+
+#define VI6_DISP_IRQ_STA 0x007c
+#define VI6_DISP_IRQ_STA_DSE (1 << 8)
+#define VI6_DISP_IRQ_STA_MAE (1 << 5)
+#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4))
+
+#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4)
+#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0)
+
+/* -----------------------------------------------------------------------------
+ * Display List Control Registers
+ */
+
+#define VI6_DL_CTRL 0x0100
+#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16)
+#define VI6_DL_CTRL_AR_WAIT_SHIFT 16
+#define VI6_DL_CTRL_DC2 (1 << 12)
+#define VI6_DL_CTRL_DC1 (1 << 8)
+#define VI6_DL_CTRL_DC0 (1 << 4)
+#define VI6_DL_CTRL_CFM0 (1 << 2)
+#define VI6_DL_CTRL_NH0 (1 << 1)
+#define VI6_DL_CTRL_DLE (1 << 0)
+
+#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4)
+
+#define VI6_DL_SWAP 0x0114
+#define VI6_DL_SWAP_LWS (1 << 2)
+#define VI6_DL_SWAP_WDS (1 << 1)
+#define VI6_DL_SWAP_BTS (1 << 0)
+
+#define VI6_DL_EXT_CTRL 0x011c
+#define VI6_DL_EXT_CTRL_NWE (1 << 16)
+#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8)
+#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8
+#define VI6_DL_EXT_CTRL_DLPRI (1 << 5)
+#define VI6_DL_EXT_CTRL_EXPRI (1 << 4)
+#define VI6_DL_EXT_CTRL_EXT (1 << 0)
+
+#define VI6_DL_BODY_SIZE 0x0120
+#define VI6_DL_BODY_SIZE_UPD (1 << 24)
+#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0)
+#define VI6_DL_BODY_SIZE_BS_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * RPF Control Registers
+ */
+
+#define VI6_RPF_OFFSET 0x100
+
+#define VI6_RPF_SRC_BSIZE 0x0300
+#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16)
+#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16
+#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0)
+#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0
+
+#define VI6_RPF_SRC_ESIZE 0x0304
+#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16)
+#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16
+#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0)
+#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0
+
+#define VI6_RPF_INFMT 0x0308
+#define VI6_RPF_INFMT_VIR (1 << 28)
+#define VI6_RPF_INFMT_CIPM (1 << 16)
+#define VI6_RPF_INFMT_SPYCS (1 << 15)
+#define VI6_RPF_INFMT_SPUVS (1 << 14)
+#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12)
+#define VI6_RPF_INFMT_CEXT_EXT (1 << 12)
+#define VI6_RPF_INFMT_CEXT_ONE (2 << 12)
+#define VI6_RPF_INFMT_CEXT_MASK (3 << 12)
+#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9)
+#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9)
+#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9)
+#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9)
+#define VI6_RPF_INFMT_RDTM_MASK (7 << 9)
+#define VI6_RPF_INFMT_CSC (1 << 8)
+#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0)
+#define VI6_RPF_INFMT_RDFMT_SHIFT 0
+
+#define VI6_RPF_DSWAP 0x030c
+#define VI6_RPF_DSWAP_A_LLS (1 << 11)
+#define VI6_RPF_DSWAP_A_LWS (1 << 10)
+#define VI6_RPF_DSWAP_A_WDS (1 << 9)
+#define VI6_RPF_DSWAP_A_BTS (1 << 8)
+#define VI6_RPF_DSWAP_P_LLS (1 << 3)
+#define VI6_RPF_DSWAP_P_LWS (1 << 2)
+#define VI6_RPF_DSWAP_P_WDS (1 << 1)
+#define VI6_RPF_DSWAP_P_BTS (1 << 0)
+
+#define VI6_RPF_LOC 0x0310
+#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16)
+#define VI6_RPF_LOC_HCOORD_SHIFT 16
+#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0)
+#define VI6_RPF_LOC_VCOORD_SHIFT 0
+
+#define VI6_RPF_ALPH_SEL 0x0314
+#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28)
+#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28
+#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24)
+#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24
+#define VI6_RPF_ALPH_SEL_BSEL (1 << 23)
+#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18)
+#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18)
+#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8)
+#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8
+#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0)
+#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0
+
+#define VI6_RPF_VRTCOL_SET 0x0318
+#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24)
+#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24
+#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16)
+#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16
+#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8)
+#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8
+#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0)
+#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0
+
+#define VI6_RPF_MSK_CTRL 0x031c
+#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24)
+#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16)
+#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16
+#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8)
+#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8
+#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0)
+#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0
+
+#define VI6_RPF_MSK_SET0 0x0320
+#define VI6_RPF_MSK_SET1 0x0324
+#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24)
+#define VI6_RPF_MSK_SET_MSA_SHIFT 24
+#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16)
+#define VI6_RPF_MSK_SET_MSR_SHIFT 16
+#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8)
+#define VI6_RPF_MSK_SET_MSG_SHIFT 8
+#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0)
+#define VI6_RPF_MSK_SET_MSB_SHIFT 0
+
+#define VI6_RPF_CKEY_CTRL 0x0328
+#define VI6_RPF_CKEY_CTRL_CV (1 << 4)
+#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1)
+#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0)
+
+#define VI6_RPF_CKEY_SET0 0x032c
+#define VI6_RPF_CKEY_SET1 0x0330
+#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24)
+#define VI6_RPF_CKEY_SET_AP_SHIFT 24
+#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16)
+#define VI6_RPF_CKEY_SET_R_SHIFT 16
+#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8)
+#define VI6_RPF_CKEY_SET_GY_SHIFT 8
+#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0)
+#define VI6_RPF_CKEY_SET_B_SHIFT 0
+
+#define VI6_RPF_SRCM_PSTRIDE 0x0334
+#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16
+#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0
+
+#define VI6_RPF_SRCM_ASTRIDE 0x0338
+#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0
+
+#define VI6_RPF_SRCM_ADDR_Y 0x033c
+#define VI6_RPF_SRCM_ADDR_C0 0x0340
+#define VI6_RPF_SRCM_ADDR_C1 0x0344
+#define VI6_RPF_SRCM_ADDR_AI 0x0348
+
+/* -----------------------------------------------------------------------------
+ * WPF Control Registers
+ */
+
+#define VI6_WPF_OFFSET 0x100
+
+#define VI6_WPF_SRCRPF 0x1000
+#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28)
+#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28)
+#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2))
+#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2))
+
+#define VI6_WPF_HSZCLIP 0x1004
+#define VI6_WPF_VSZCLIP 0x1008
+#define VI6_WPF_SZCLIP_EN (1 << 28)
+#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16)
+#define VI6_WPF_SZCLIP_OFST_SHIFT 16
+#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0)
+#define VI6_WPF_SZCLIP_SIZE_SHIFT 0
+
+#define VI6_WPF_OUTFMT 0x100c
+#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24)
+#define VI6_WPF_OUTFMT_PDV_SHIFT 24
+#define VI6_WPF_OUTFMT_PXA (1 << 23)
+#define VI6_WPF_OUTFMT_FLP (1 << 16)
+#define VI6_WPF_OUTFMT_SPYCS (1 << 15)
+#define VI6_WPF_OUTFMT_SPUVS (1 << 14)
+#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12)
+#define VI6_WPF_OUTFMT_DITH_EN (3 << 12)
+#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12)
+#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9)
+#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9)
+#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9)
+#define VI6_WPF_OUTFMT_CSC (1 << 8)
+#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0)
+#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0
+
+#define VI6_WPF_DSWAP 0x1010
+#define VI6_WPF_DSWAP_P_LLS (1 << 3)
+#define VI6_WPF_DSWAP_P_LWS (1 << 2)
+#define VI6_WPF_DSWAP_P_WDS (1 << 1)
+#define VI6_WPF_DSWAP_P_BTS (1 << 0)
+
+#define VI6_WPF_RNDCTRL 0x1014
+#define VI6_WPF_RNDCTRL_CBRM (1 << 28)
+#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24)
+#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24)
+#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16)
+#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16
+#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12)
+#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12)
+
+#define VI6_WPF_DSTM_STRIDE_Y 0x101c
+#define VI6_WPF_DSTM_STRIDE_C 0x1020
+#define VI6_WPF_DSTM_ADDR_Y 0x1024
+#define VI6_WPF_DSTM_ADDR_C0 0x1028
+#define VI6_WPF_DSTM_ADDR_C1 0x102c
+
+#define VI6_WPF_WRBCK_CTRL 0x1034
+#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
+
+/* -----------------------------------------------------------------------------
+ * DPR Control Registers
+ */
+
+#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4)
+
+#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4)
+#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8)
+
+#define VI6_DPR_SRU_ROUTE 0x2024
+#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4)
+#define VI6_DPR_LUT_ROUTE 0x203c
+#define VI6_DPR_CLU_ROUTE 0x2040
+#define VI6_DPR_HST_ROUTE 0x2044
+#define VI6_DPR_HSI_ROUTE 0x2048
+#define VI6_DPR_BRU_ROUTE 0x204c
+#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8)
+#define VI6_DPR_ROUTE_FXA_SHIFT 16
+#define VI6_DPR_ROUTE_FP_MASK (0xff << 8)
+#define VI6_DPR_ROUTE_FP_SHIFT 8
+#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0)
+#define VI6_DPR_ROUTE_RT_SHIFT 0
+
+#define VI6_DPR_HGO_SMPPT 0x2050
+#define VI6_DPR_HGT_SMPPT 0x2054
+#define VI6_DPR_SMPPT_TGW_MASK (7 << 8)
+#define VI6_DPR_SMPPT_TGW_SHIFT 8
+#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
+#define VI6_DPR_SMPPT_PT_SHIFT 0
+
+#define VI6_DPR_NODE_RPF(n) (n)
+#define VI6_DPR_NODE_SRU 16
+#define VI6_DPR_NODE_UDS(n) (17 + (n))
+#define VI6_DPR_NODE_LUT 22
+#define VI6_DPR_NODE_BRU_IN(n) (23 + (n))
+#define VI6_DPR_NODE_BRU_OUT 27
+#define VI6_DPR_NODE_CLU 29
+#define VI6_DPR_NODE_HST 30
+#define VI6_DPR_NODE_HSI 31
+#define VI6_DPR_NODE_LIF 55
+#define VI6_DPR_NODE_WPF(n) (56 + (n))
+#define VI6_DPR_NODE_UNUSED 63
+
+/* -----------------------------------------------------------------------------
+ * SRU Control Registers
+ */
+
+#define VI6_SRU_CTRL0 0x2200
+#define VI6_SRU_CTRL1 0x2204
+#define VI6_SRU_CTRL2 0x2208
+
+/* -----------------------------------------------------------------------------
+ * UDS Control Registers
+ */
+
+#define VI6_UDS_OFFSET 0x100
+
+#define VI6_UDS_CTRL 0x2300
+#define VI6_UDS_CTRL_AMD (1 << 30)
+#define VI6_UDS_CTRL_FMD (1 << 29)
+#define VI6_UDS_CTRL_BLADV (1 << 28)
+#define VI6_UDS_CTRL_AON (1 << 25)
+#define VI6_UDS_CTRL_ATHON (1 << 24)
+#define VI6_UDS_CTRL_BC (1 << 20)
+#define VI6_UDS_CTRL_NE_A (1 << 19)
+#define VI6_UDS_CTRL_NE_RCR (1 << 18)
+#define VI6_UDS_CTRL_NE_GY (1 << 17)
+#define VI6_UDS_CTRL_NE_BCB (1 << 16)
+#define VI6_UDS_CTRL_TDIPC (1 << 1)
+
+#define VI6_UDS_SCALE 0x2304
+#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28)
+#define VI6_UDS_SCALE_HMANT_SHIFT 28
+#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16)
+#define VI6_UDS_SCALE_HFRAC_SHIFT 16
+#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12)
+#define VI6_UDS_SCALE_VMANT_SHIFT 12
+#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0)
+#define VI6_UDS_SCALE_VFRAC_SHIFT 0
+
+#define VI6_UDS_ALPTH 0x2308
+#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8)
+#define VI6_UDS_ALPTH_TH1_SHIFT 8
+#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0)
+#define VI6_UDS_ALPTH_TH0_SHIFT 0
+
+#define VI6_UDS_ALPVAL 0x230c
+#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16)
+#define VI6_UDS_ALPVAL_VAL2_SHIFT 16
+#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8)
+#define VI6_UDS_ALPVAL_VAL1_SHIFT 8
+#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0)
+#define VI6_UDS_ALPVAL_VAL0_SHIFT 0
+
+#define VI6_UDS_PASS_BWIDTH 0x2310
+#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16)
+#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16
+#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0)
+#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0
+
+#define VI6_UDS_IPC 0x2318
+#define VI6_UDS_IPC_FIELD (1 << 27)
+#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0)
+#define VI6_UDS_IPC_VEDP_SHIFT 0
+
+#define VI6_UDS_CLIP_SIZE 0x2324
+#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16)
+#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16
+#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0)
+#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0
+
+#define VI6_UDS_FILL_COLOR 0x2328
+#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16)
+#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16
+#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8)
+#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8
+#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0)
+#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * LUT Control Registers
+ */
+
+#define VI6_LUT_CTRL 0x2800
+
+/* -----------------------------------------------------------------------------
+ * CLU Control Registers
+ */
+
+#define VI6_CLU_CTRL 0x2900
+
+/* -----------------------------------------------------------------------------
+ * HST Control Registers
+ */
+
+#define VI6_HST_CTRL 0x2a00
+
+/* -----------------------------------------------------------------------------
+ * HSI Control Registers
+ */
+
+#define VI6_HSI_CTRL 0x2b00
+
+/* -----------------------------------------------------------------------------
+ * BRU Control Registers
+ */
+
+#define VI6_BRU_INCTRL 0x2c00
+#define VI6_BRU_VIRRPF_SIZE 0x2c04
+#define VI6_BRU_VIRRPF_LOC 0x2c08
+#define VI6_BRU_VIRRPF_COL 0x2c0c
+#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8)
+#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8)
+#define VI6_BRU_ROP 0x2c30
+
+/* -----------------------------------------------------------------------------
+ * HGO Control Registers
+ */
+
+#define VI6_HGO_OFFSET 0x3000
+#define VI6_HGO_SIZE 0x3004
+#define VI6_HGO_MODE 0x3008
+#define VI6_HGO_LB_TH 0x300c
+#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8)
+#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8)
+#define VI6_HGO_R_HISTO 0x3030
+#define VI6_HGO_R_MAXMIN 0x3130
+#define VI6_HGO_R_SUM 0x3134
+#define VI6_HGO_R_LB_DET 0x3138
+#define VI6_HGO_G_HISTO 0x3140
+#define VI6_HGO_G_MAXMIN 0x3240
+#define VI6_HGO_G_SUM 0x3244
+#define VI6_HGO_G_LB_DET 0x3248
+#define VI6_HGO_B_HISTO 0x3250
+#define VI6_HGO_B_MAXMIN 0x3350
+#define VI6_HGO_B_SUM 0x3354
+#define VI6_HGO_B_LB_DET 0x3358
+#define VI6_HGO_REGRST 0x33fc
+
+/* -----------------------------------------------------------------------------
+ * HGT Control Registers
+ */
+
+#define VI6_HGT_OFFSET 0x3400
+#define VI6_HGT_SIZE 0x3404
+#define VI6_HGT_MODE 0x3408
+#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4)
+#define VI6_HGT_LB_TH 0x3424
+#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8)
+#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8)
+#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4)
+#define VI6_HGT_MAXMIN 0x3750
+#define VI6_HGT_SUM 0x3754
+#define VI6_HGT_LB_DET 0x3758
+#define VI6_HGT_REGRST 0x37fc
+
+/* -----------------------------------------------------------------------------
+ * LIF Control Registers
+ */
+
+#define VI6_LIF_CTRL 0x3b00
+#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16)
+#define VI6_LIF_CTRL_OBTH_SHIFT 16
+#define VI6_LIF_CTRL_CFMT (1 << 4)
+#define VI6_LIF_CTRL_REQSEL (1 << 1)
+#define VI6_LIF_CTRL_LIF_EN (1 << 0)
+
+#define VI6_LIF_CSBTH 0x3b04
+#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16)
+#define VI6_LIF_CSBTH_HBTH_SHIFT 16
+#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0)
+#define VI6_LIF_CSBTH_LBTH_SHIFT 0
+
+/* -----------------------------------------------------------------------------
+ * Security Control Registers
+ */
+
+#define VI6_SECURITY_CTRL0 0x3d00
+#define VI6_SECURITY_CTRL1 0x3d04
+
+/* -----------------------------------------------------------------------------
+ * RPF CLUT Registers
+ */
+
+#define VI6_CLUT_TABLE 0x4000
+
+/* -----------------------------------------------------------------------------
+ * 1D LUT Registers
+ */
+
+#define VI6_LUT_TABLE 0x7000
+
+/* -----------------------------------------------------------------------------
+ * 3D LUT Registers
+ */
+
+#define VI6_CLU_ADDR 0x7400
+#define VI6_CLU_DATA 0x7404
+
+/* -----------------------------------------------------------------------------
+ * Formats
+ */
+
+#define VI6_FMT_RGB_332 0x00
+#define VI6_FMT_XRGB_4444 0x01
+#define VI6_FMT_RGBX_4444 0x02
+#define VI6_FMT_XRGB_1555 0x04
+#define VI6_FMT_RGBX_5551 0x05
+#define VI6_FMT_RGB_565 0x06
+#define VI6_FMT_AXRGB_86666 0x07
+#define VI6_FMT_RGBXA_66668 0x08
+#define VI6_FMT_XRGBA_66668 0x09
+#define VI6_FMT_ARGBX_86666 0x0a
+#define VI6_FMT_AXRXGXB_8262626 0x0b
+#define VI6_FMT_XRXGXBA_2626268 0x0c
+#define VI6_FMT_ARXGXBX_8626262 0x0d
+#define VI6_FMT_RXGXBXA_6262628 0x0e
+#define VI6_FMT_XRGB_6666 0x0f
+#define VI6_FMT_RGBX_6666 0x10
+#define VI6_FMT_XRXGXB_262626 0x11
+#define VI6_FMT_RXGXBX_626262 0x12
+#define VI6_FMT_ARGB_8888 0x13
+#define VI6_FMT_RGBA_8888 0x14
+#define VI6_FMT_RGB_888 0x15
+#define VI6_FMT_XRGXGB_763763 0x16
+#define VI6_FMT_XXRGB_86666 0x17
+#define VI6_FMT_BGR_888 0x18
+#define VI6_FMT_ARGB_4444 0x19
+#define VI6_FMT_RGBA_4444 0x1a
+#define VI6_FMT_ARGB_1555 0x1b
+#define VI6_FMT_RGBA_5551 0x1c
+#define VI6_FMT_ABGR_4444 0x1d
+#define VI6_FMT_BGRA_4444 0x1e
+#define VI6_FMT_ABGR_1555 0x1f
+#define VI6_FMT_BGRA_5551 0x20
+#define VI6_FMT_XBXGXR_262626 0x21
+#define VI6_FMT_ABGR_8888 0x22
+#define VI6_FMT_XXRGB_88565 0x23
+
+#define VI6_FMT_Y_UV_444 0x40
+#define VI6_FMT_Y_UV_422 0x41
+#define VI6_FMT_Y_UV_420 0x42
+#define VI6_FMT_YUV_444 0x46
+#define VI6_FMT_YUYV_422 0x47
+#define VI6_FMT_YYUV_422 0x48
+#define VI6_FMT_YUV_420 0x49
+#define VI6_FMT_Y_U_V_444 0x4a
+#define VI6_FMT_Y_U_V_422 0x4b
+#define VI6_FMT_Y_U_V_420 0x4c
+
+#endif /* __VSP1_REGS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c
new file mode 100644
index 000000000000..254871d3423e
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rpf.c
@@ -0,0 +1,209 @@
+/*
+ * vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define RPF_MAX_WIDTH 8190
+#define RPF_MAX_HEIGHT 8190
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg)
+{
+ return vsp1_read(rpf->entity.vsp1,
+ reg + rpf->entity.index * VI6_RPF_OFFSET);
+}
+
+static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
+{
+ vsp1_write(rpf->entity.vsp1,
+ reg + rpf->entity.index * VI6_RPF_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_rwpf *rpf = to_rwpf(subdev);
+ const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
+ const struct v4l2_pix_format_mplane *format = &rpf->video.format;
+ u32 pstride;
+ u32 infmt;
+
+ if (!enable)
+ return 0;
+
+ /* Source size and stride. Cropping isn't supported yet. */
+ vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
+ (format->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
+ (format->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
+ vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
+ (format->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
+ (format->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
+
+ pstride = format->plane_fmt[0].bytesperline
+ << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
+ if (format->num_planes > 1)
+ pstride |= format->plane_fmt[1].bytesperline
+ << VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
+
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
+
+ /* Format */
+ infmt = VI6_RPF_INFMT_CIPM
+ | (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
+
+ if (fmtinfo->swap_yc)
+ infmt |= VI6_RPF_INFMT_SPYCS;
+ if (fmtinfo->swap_uv)
+ infmt |= VI6_RPF_INFMT_SPUVS;
+
+ if (rpf->entity.formats[RWPF_PAD_SINK].code !=
+ rpf->entity.formats[RWPF_PAD_SOURCE].code)
+ infmt |= VI6_RPF_INFMT_CSC;
+
+ vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
+ vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
+
+ /* Output location. Composing isn't supported yet. */
+ vsp1_rpf_write(rpf, VI6_RPF_LOC, 0);
+
+ /* Disable alpha, mask and color key. Set the alpha channel to a fixed
+ * value of 255.
+ */
+ vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_ASEL_FIXED);
+ vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
+ 255 << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
+ vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
+ vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops rpf_video_ops = {
+ .s_stream = rpf_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops rpf_pad_ops = {
+ .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
+ .enum_frame_size = vsp1_rwpf_enum_frame_size,
+ .get_fmt = vsp1_rwpf_get_format,
+ .set_fmt = vsp1_rwpf_set_format,
+};
+
+static struct v4l2_subdev_ops rpf_ops = {
+ .video = &rpf_video_ops,
+ .pad = &rpf_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video Device Operations
+ */
+
+static void rpf_vdev_queue(struct vsp1_video *video,
+ struct vsp1_video_buffer *buf)
+{
+ struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
+
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0]);
+ if (buf->buf.num_planes > 1)
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, buf->addr[1]);
+ if (buf->buf.num_planes > 2)
+ vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, buf->addr[2]);
+}
+
+static const struct vsp1_video_operations rpf_vdev_ops = {
+ .queue = rpf_vdev_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_video *video;
+ struct vsp1_rwpf *rpf;
+ int ret;
+
+ rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
+ if (rpf == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rpf->max_width = RPF_MAX_WIDTH;
+ rpf->max_height = RPF_MAX_HEIGHT;
+
+ rpf->entity.type = VSP1_ENTITY_RPF;
+ rpf->entity.index = index;
+ rpf->entity.id = VI6_DPR_NODE_RPF(index);
+
+ ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &rpf->entity.subdev;
+ v4l2_subdev_init(subdev, &rpf_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, rpf);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ /* Initialize the video device. */
+ video = &rpf->video;
+
+ video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ video->vsp1 = vsp1;
+ video->ops = &rpf_vdev_ops;
+
+ ret = vsp1_video_init(video, &rpf->entity);
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the video device to the RPF. */
+ ret = media_entity_create_link(&rpf->video.video.entity, 0,
+ &rpf->entity.subdev.entity,
+ RWPF_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret < 0)
+ goto error_link;
+
+ return rpf;
+
+error_link:
+ vsp1_video_cleanup(video);
+error_video:
+ media_entity_cleanup(&rpf->entity.subdev.entity);
+ return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
new file mode 100644
index 000000000000..9752d5516ceb
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -0,0 +1,124 @@
+/*
+ * vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define RWPF_MIN_WIDTH 1
+#define RWPF_MIN_HEIGHT 1
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+
+ return 0;
+}
+
+int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, fse->pad);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == RWPF_PAD_SINK) {
+ fse->min_width = RWPF_MIN_WIDTH;
+ fse->max_width = rwpf->max_width;
+ fse->min_height = RWPF_MIN_HEIGHT;
+ fse->max_height = rwpf->max_height;
+ } else {
+ /* The size on the source pad are fixed and always identical to
+ * the size on the sink pad.
+ */
+ fse->min_width = format->width;
+ fse->max_width = format->width;
+ fse->min_height = format->height;
+ fse->max_height = format->height;
+ }
+
+ return 0;
+}
+
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_rwpf *rwpf = to_rwpf(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
+ fmt->which);
+
+ if (fmt->pad == RWPF_PAD_SOURCE) {
+ /* The RWPF performs format conversion but can't scale, only the
+ * format code can be changed on the source pad.
+ */
+ format->code = fmt->format.code;
+ fmt->format = *format;
+ return 0;
+ }
+
+ format->code = fmt->format.code;
+ format->width = clamp_t(unsigned int, fmt->format.width,
+ RWPF_MIN_WIDTH, rwpf->max_width);
+ format->height = clamp_t(unsigned int, fmt->format.height,
+ RWPF_MIN_HEIGHT, rwpf->max_height);
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ fmt->format = *format;
+
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
+ fmt->which);
+ *format = fmt->format;
+
+ return 0;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h
new file mode 100644
index 000000000000..c182d85f36b3
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.h
@@ -0,0 +1,53 @@
+/*
+ * vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_RWPF_H__
+#define __VSP1_RWPF_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_entity.h"
+#include "vsp1_video.h"
+
+#define RWPF_PAD_SINK 0
+#define RWPF_PAD_SOURCE 1
+
+struct vsp1_rwpf {
+ struct vsp1_entity entity;
+ struct vsp1_video video;
+
+ unsigned int max_width;
+ unsigned int max_height;
+};
+
+static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_rwpf, entity.subdev);
+}
+
+struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
+struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
+
+int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code);
+int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse);
+int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt);
+int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt);
+
+#endif /* __VSP1_RWPF_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/vsp1/vsp1_uds.c
new file mode 100644
index 000000000000..0e50b37f060d
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uds.c
@@ -0,0 +1,346 @@
+/*
+ * vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/gfp.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_uds.h"
+
+#define UDS_MIN_SIZE 4U
+#define UDS_MAX_SIZE 8190U
+
+#define UDS_MIN_FACTOR 0x0100
+#define UDS_MAX_FACTOR 0xffff
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg)
+{
+ return vsp1_read(uds->entity.vsp1,
+ reg + uds->entity.index * VI6_UDS_OFFSET);
+}
+
+static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
+{
+ vsp1_write(uds->entity.vsp1,
+ reg + uds->entity.index * VI6_UDS_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * Scaling Computation
+ */
+
+/*
+ * uds_output_size - Return the output size for an input size and scaling ratio
+ * @input: input size in pixels
+ * @ratio: scaling ratio in U4.12 fixed-point format
+ */
+static unsigned int uds_output_size(unsigned int input, unsigned int ratio)
+{
+ if (ratio > 4096) {
+ /* Down-scaling */
+ unsigned int mp;
+
+ mp = ratio / 4096;
+ mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
+
+ return (input - 1) / mp * mp * 4096 / ratio + 1;
+ } else {
+ /* Up-scaling */
+ return (input - 1) * 4096 / ratio + 1;
+ }
+}
+
+/*
+ * uds_output_limits - Return the min and max output sizes for an input size
+ * @input: input size in pixels
+ * @minimum: minimum output size (returned)
+ * @maximum: maximum output size (returned)
+ */
+static void uds_output_limits(unsigned int input,
+ unsigned int *minimum, unsigned int *maximum)
+{
+ *minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE);
+ *maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE);
+}
+
+/*
+ * uds_passband_width - Return the passband filter width for a scaling ratio
+ * @ratio: scaling ratio in U4.12 fixed-point format
+ */
+static unsigned int uds_passband_width(unsigned int ratio)
+{
+ if (ratio >= 4096) {
+ /* Down-scaling */
+ unsigned int mp;
+
+ mp = ratio / 4096;
+ mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
+
+ return 64 * 4096 * mp / ratio;
+ } else {
+ /* Up-scaling */
+ return 64;
+ }
+}
+
+static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
+{
+ /* TODO: This is an approximation that will need to be refined. */
+ return (input - 1) * 4096 / (output - 1);
+}
+
+static void uds_compute_ratios(struct vsp1_uds *uds)
+{
+ struct v4l2_mbus_framefmt *input = &uds->entity.formats[UDS_PAD_SINK];
+ struct v4l2_mbus_framefmt *output =
+ &uds->entity.formats[UDS_PAD_SOURCE];
+
+ uds->hscale = uds_compute_ratio(input->width, output->width);
+ uds->vscale = uds_compute_ratio(input->height, output->height);
+
+ dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n",
+ uds->hscale, uds->vscale);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_uds *uds = to_uds(subdev);
+
+ if (!enable)
+ return 0;
+
+ /* Enable multi-tap scaling. */
+ vsp1_uds_write(uds, VI6_UDS_CTRL, VI6_UDS_CTRL_BC);
+
+ vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
+ (uds_passband_width(uds->hscale)
+ << VI6_UDS_PASS_BWIDTH_H_SHIFT) |
+ (uds_passband_width(uds->vscale)
+ << VI6_UDS_PASS_BWIDTH_V_SHIFT));
+
+
+ /* Set the scaling ratios and the output size. */
+ format = &uds->entity.formats[UDS_PAD_SOURCE];
+
+ vsp1_uds_write(uds, VI6_UDS_SCALE,
+ (uds->hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
+ (uds->vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
+ vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
+ (format->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
+ (format->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Pad Operations
+ */
+
+static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ static const unsigned int codes[] = {
+ V4L2_MBUS_FMT_ARGB8888_1X32,
+ V4L2_MBUS_FMT_AYUV8_1X32,
+ };
+
+ if (code->pad == UDS_PAD_SINK) {
+ if (code->index >= ARRAY_SIZE(codes))
+ return -EINVAL;
+
+ code->code = codes[code->index];
+ } else {
+ struct v4l2_mbus_framefmt *format;
+
+ /* The UDS can't perform format conversion, the sink format is
+ * always identical to the source format.
+ */
+ if (code->index)
+ return -EINVAL;
+
+ format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+ code->code = format->code;
+ }
+
+ return 0;
+}
+
+static int uds_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
+
+ if (fse->index || fse->code != format->code)
+ return -EINVAL;
+
+ if (fse->pad == UDS_PAD_SINK) {
+ fse->min_width = UDS_MIN_SIZE;
+ fse->max_width = UDS_MAX_SIZE;
+ fse->min_height = UDS_MIN_SIZE;
+ fse->max_height = UDS_MAX_SIZE;
+ } else {
+ uds_output_limits(format->width, &fse->min_width,
+ &fse->max_width);
+ uds_output_limits(format->height, &fse->min_height,
+ &fse->max_height);
+ }
+
+ return 0;
+}
+
+static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_uds *uds = to_uds(subdev);
+
+ fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+ fmt->which);
+
+ return 0;
+}
+
+static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh,
+ unsigned int pad, struct v4l2_mbus_framefmt *fmt,
+ enum v4l2_subdev_format_whence which)
+{
+ struct v4l2_mbus_framefmt *format;
+ unsigned int minimum;
+ unsigned int maximum;
+
+ switch (pad) {
+ case UDS_PAD_SINK:
+ /* Default to YUV if the requested format is not supported. */
+ if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
+ fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
+ fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
+
+ fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
+ fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
+ break;
+
+ case UDS_PAD_SOURCE:
+ /* The UDS scales but can't perform format conversion. */
+ format = vsp1_entity_get_pad_format(&uds->entity, fh,
+ UDS_PAD_SINK, which);
+ fmt->code = format->code;
+
+ uds_output_limits(format->width, &minimum, &maximum);
+ fmt->width = clamp(fmt->width, minimum, maximum);
+ uds_output_limits(format->height, &minimum, &maximum);
+ fmt->height = clamp(fmt->height, minimum, maximum);
+ break;
+ }
+
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct vsp1_uds *uds = to_uds(subdev);
+ struct v4l2_mbus_framefmt *format;
+
+ uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which);
+
+ format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
+ fmt->which);
+ *format = fmt->format;
+
+ if (fmt->pad == UDS_PAD_SINK) {
+ /* Propagate the format to the source pad. */
+ format = vsp1_entity_get_pad_format(&uds->entity, fh,
+ UDS_PAD_SOURCE, fmt->which);
+ *format = fmt->format;
+
+ uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
+ }
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ uds_compute_ratios(uds);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops uds_video_ops = {
+ .s_stream = uds_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops uds_pad_ops = {
+ .enum_mbus_code = uds_enum_mbus_code,
+ .enum_frame_size = uds_enum_frame_size,
+ .get_fmt = uds_get_format,
+ .set_fmt = uds_set_format,
+};
+
+static struct v4l2_subdev_ops uds_ops = {
+ .video = &uds_video_ops,
+ .pad = &uds_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_uds *uds;
+ int ret;
+
+ uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);
+ if (uds == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ uds->entity.type = VSP1_ENTITY_UDS;
+ uds->entity.index = index;
+ uds->entity.id = VI6_DPR_NODE_UDS(index);
+
+ ret = vsp1_entity_init(vsp1, &uds->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &uds->entity.subdev;
+ v4l2_subdev_init(subdev, &uds_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, uds);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ return uds;
+}
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/vsp1/vsp1_uds.h
new file mode 100644
index 000000000000..972a285abdb9
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_uds.h
@@ -0,0 +1,40 @@
+/*
+ * vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_UDS_H__
+#define __VSP1_UDS_H__
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+struct vsp1_device;
+
+#define UDS_PAD_SINK 0
+#define UDS_PAD_SOURCE 1
+
+struct vsp1_uds {
+ struct vsp1_entity entity;
+
+ unsigned int hscale;
+ unsigned int vscale;
+};
+
+static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_uds, entity.subdev);
+}
+
+struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
+
+#endif /* __VSP1_UDS_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
new file mode 100644
index 000000000000..714c53ef6c11
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -0,0 +1,1069 @@
+/*
+ * vsp1_video.c -- R-Car VSP1 Video Node
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+#include <linux/videodev2.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "vsp1.h"
+#include "vsp1_entity.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define VSP1_VIDEO_DEF_FORMAT V4L2_PIX_FMT_YUYV
+#define VSP1_VIDEO_DEF_WIDTH 1024
+#define VSP1_VIDEO_DEF_HEIGHT 768
+
+#define VSP1_VIDEO_MIN_WIDTH 2U
+#define VSP1_VIDEO_MAX_WIDTH 8190U
+#define VSP1_VIDEO_MIN_HEIGHT 2U
+#define VSP1_VIDEO_MAX_HEIGHT 8190U
+
+/* -----------------------------------------------------------------------------
+ * Helper functions
+ */
+
+static const struct vsp1_format_info vsp1_video_formats[] = {
+ { V4L2_PIX_FMT_RGB332, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_332, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 8, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB444, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_4444, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB555, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_XRGB_1555, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB565, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_565, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS,
+ 1, { 16, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_BGR24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_BGR_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB24, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_BGR32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
+ 1, { 32, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_RGB32, V4L2_MBUS_FMT_ARGB8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1 },
+ { V4L2_PIX_FMT_UYVY, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, false, 2, 1 },
+ { V4L2_PIX_FMT_VYUY, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, true, 2, 1 },
+ { V4L2_PIX_FMT_YUYV, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, false, 2, 1 },
+ { V4L2_PIX_FMT_YVYU, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, true, true, 2, 1 },
+ { V4L2_PIX_FMT_NV12M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 2 },
+ { V4L2_PIX_FMT_NV21M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 2 },
+ { V4L2_PIX_FMT_NV16M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, false, 2, 1 },
+ { V4L2_PIX_FMT_NV61M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_UV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 2, { 8, 16, 0 }, false, true, 2, 1 },
+ { V4L2_PIX_FMT_YUV420M, V4L2_MBUS_FMT_AYUV8_1X32,
+ VI6_FMT_Y_U_V_420, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 3, { 8, 8, 8 }, false, false, 2, 2 },
+};
+
+/*
+ * vsp1_get_format_info - Retrieve format information for a 4CC
+ * @fourcc: the format 4CC
+ *
+ * Return a pointer to the format information structure corresponding to the
+ * given V4L2 format 4CC, or NULL if no corresponding format can be found.
+ */
+static const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
+ const struct vsp1_format_info *info = &vsp1_video_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+
+ return NULL;
+}
+
+
+static struct v4l2_subdev *
+vsp1_video_remote_subdev(struct media_pad *local, u32 *pad)
+{
+ struct media_pad *remote;
+
+ remote = media_entity_remote_pad(local);
+ if (remote == NULL ||
+ media_entity_type(remote->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return NULL;
+
+ if (pad)
+ *pad = remote->index;
+
+ return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int vsp1_video_verify_format(struct vsp1_video *video)
+{
+ struct v4l2_subdev_format fmt;
+ struct v4l2_subdev *subdev;
+ int ret;
+
+ subdev = vsp1_video_remote_subdev(&video->pad, &fmt.pad);
+ if (subdev == NULL)
+ return -EINVAL;
+
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+ if (video->fmtinfo->mbus != fmt.format.code ||
+ video->format.height != fmt.format.height ||
+ video->format.width != fmt.format.width)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int __vsp1_video_try_format(struct vsp1_video *video,
+ struct v4l2_pix_format_mplane *pix,
+ const struct vsp1_format_info **fmtinfo)
+{
+ const struct vsp1_format_info *info;
+ unsigned int width = pix->width;
+ unsigned int height = pix->height;
+ unsigned int i;
+
+ /* Retrieve format information and select the default format if the
+ * requested format isn't supported.
+ */
+ info = vsp1_get_format_info(pix->pixelformat);
+ if (info == NULL)
+ info = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+
+ pix->pixelformat = info->fourcc;
+ pix->colorspace = V4L2_COLORSPACE_SRGB;
+ pix->field = V4L2_FIELD_NONE;
+ memset(pix->reserved, 0, sizeof(pix->reserved));
+
+ /* Align the width and height for YUV 4:2:2 and 4:2:0 formats. */
+ width = round_down(width, info->hsub);
+ height = round_down(height, info->vsub);
+
+ /* Clamp the width and height. */
+ pix->width = clamp(width, VSP1_VIDEO_MIN_WIDTH, VSP1_VIDEO_MAX_WIDTH);
+ pix->height = clamp(height, VSP1_VIDEO_MIN_HEIGHT,
+ VSP1_VIDEO_MAX_HEIGHT);
+
+ /* Compute and clamp the stride and image size. While not documented in
+ * the datasheet, strides not aligned to a multiple of 128 bytes result
+ * in image corruption.
+ */
+ for (i = 0; i < max(info->planes, 2U); ++i) {
+ unsigned int hsub = i > 0 ? info->hsub : 1;
+ unsigned int vsub = i > 0 ? info->vsub : 1;
+ unsigned int align = 128;
+ unsigned int bpl;
+
+ bpl = clamp_t(unsigned int, pix->plane_fmt[i].bytesperline,
+ pix->width / hsub * info->bpp[i] / 8,
+ round_down(65535U, align));
+
+ pix->plane_fmt[i].bytesperline = round_up(bpl, align);
+ pix->plane_fmt[i].sizeimage = pix->plane_fmt[i].bytesperline
+ * pix->height / vsub;
+ }
+
+ if (info->planes == 3) {
+ /* The second and third planes must have the same stride. */
+ pix->plane_fmt[2].bytesperline = pix->plane_fmt[1].bytesperline;
+ pix->plane_fmt[2].sizeimage = pix->plane_fmt[1].sizeimage;
+ }
+
+ pix->num_planes = info->planes;
+
+ if (fmtinfo)
+ *fmtinfo = info;
+
+ return 0;
+}
+
+static bool
+vsp1_video_format_adjust(struct vsp1_video *video,
+ const struct v4l2_pix_format_mplane *format,
+ struct v4l2_pix_format_mplane *adjust)
+{
+ unsigned int i;
+
+ *adjust = *format;
+ __vsp1_video_try_format(video, adjust, NULL);
+
+ if (format->width != adjust->width ||
+ format->height != adjust->height ||
+ format->pixelformat != adjust->pixelformat ||
+ format->num_planes != adjust->num_planes)
+ return false;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ if (format->plane_fmt[i].bytesperline !=
+ adjust->plane_fmt[i].bytesperline)
+ return false;
+
+ adjust->plane_fmt[i].sizeimage =
+ max(adjust->plane_fmt[i].sizeimage,
+ format->plane_fmt[i].sizeimage);
+ }
+
+ return true;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipeline Management
+ */
+
+static int vsp1_pipeline_validate_branch(struct vsp1_rwpf *input,
+ struct vsp1_rwpf *output)
+{
+ struct vsp1_entity *entity;
+ unsigned int entities = 0;
+ struct media_pad *pad;
+ bool uds_found = false;
+
+ pad = media_entity_remote_pad(&input->entity.pads[RWPF_PAD_SOURCE]);
+
+ while (1) {
+ if (pad == NULL)
+ return -EPIPE;
+
+ /* We've reached a video node, that shouldn't have happened. */
+ if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV)
+ return -EPIPE;
+
+ entity = to_vsp1_entity(media_entity_to_v4l2_subdev(pad->entity));
+
+ /* We've reached the WPF, we're done. */
+ if (entity->type == VSP1_ENTITY_WPF)
+ break;
+
+ /* Ensure the branch has no loop. */
+ if (entities & (1 << entity->subdev.entity.id))
+ return -EPIPE;
+
+ entities |= 1 << entity->subdev.entity.id;
+
+ /* UDS can't be chained. */
+ if (entity->type == VSP1_ENTITY_UDS) {
+ if (uds_found)
+ return -EPIPE;
+ uds_found = true;
+ }
+
+ /* Follow the source link. The link setup operations ensure
+ * that the output fan-out can't be more than one, there is thus
+ * no need to verify here that only a single source link is
+ * activated.
+ */
+ pad = &entity->pads[entity->source_pad];
+ pad = media_entity_remote_pad(pad);
+ }
+
+ /* The last entity must be the output WPF. */
+ if (entity != &output->entity)
+ return -EPIPE;
+
+ return 0;
+}
+
+static int vsp1_pipeline_validate(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ struct media_entity_graph graph;
+ struct media_entity *entity = &video->video.entity;
+ struct media_device *mdev = entity->parent;
+ unsigned int i;
+ int ret;
+
+ mutex_lock(&mdev->graph_mutex);
+
+ /* Walk the graph to locate the entities and video nodes. */
+ media_entity_graph_walk_start(&graph, entity);
+
+ while ((entity = media_entity_graph_walk_next(&graph))) {
+ struct v4l2_subdev *subdev;
+ struct vsp1_rwpf *rwpf;
+ struct vsp1_entity *e;
+
+ if (media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) {
+ pipe->num_video++;
+ continue;
+ }
+
+ subdev = media_entity_to_v4l2_subdev(entity);
+ e = to_vsp1_entity(subdev);
+ list_add_tail(&e->list_pipe, &pipe->entities);
+
+ if (e->type == VSP1_ENTITY_RPF) {
+ rwpf = to_rwpf(subdev);
+ pipe->inputs[pipe->num_inputs++] = rwpf;
+ rwpf->video.pipe_index = pipe->num_inputs;
+ } else if (e->type == VSP1_ENTITY_WPF) {
+ rwpf = to_rwpf(subdev);
+ pipe->output = to_rwpf(subdev);
+ rwpf->video.pipe_index = 0;
+ } else if (e->type == VSP1_ENTITY_LIF) {
+ pipe->lif = e;
+ }
+ }
+
+ mutex_unlock(&mdev->graph_mutex);
+
+ /* We need one output and at least one input. */
+ if (pipe->num_inputs == 0 || !pipe->output) {
+ ret = -EPIPE;
+ goto error;
+ }
+
+ /* Follow links downstream for each input and make sure the graph
+ * contains no loop and that all branches end at the output WPF.
+ */
+ for (i = 0; i < pipe->num_inputs; ++i) {
+ ret = vsp1_pipeline_validate_branch(pipe->inputs[i],
+ pipe->output);
+ if (ret < 0)
+ goto error;
+ }
+
+ return 0;
+
+error:
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->buffers_ready = 0;
+ pipe->num_video = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->lif = NULL;
+ return ret;
+}
+
+static int vsp1_pipeline_init(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ int ret;
+
+ mutex_lock(&pipe->lock);
+
+ /* If we're the first user validate and initialize the pipeline. */
+ if (pipe->use_count == 0) {
+ ret = vsp1_pipeline_validate(pipe, video);
+ if (ret < 0)
+ goto done;
+ }
+
+ pipe->use_count++;
+ ret = 0;
+
+done:
+ mutex_unlock(&pipe->lock);
+ return ret;
+}
+
+static void vsp1_pipeline_cleanup(struct vsp1_pipeline *pipe)
+{
+ mutex_lock(&pipe->lock);
+
+ /* If we're the last user clean up the pipeline. */
+ if (--pipe->use_count == 0) {
+ INIT_LIST_HEAD(&pipe->entities);
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ pipe->buffers_ready = 0;
+ pipe->num_video = 0;
+ pipe->num_inputs = 0;
+ pipe->output = NULL;
+ pipe->lif = NULL;
+ }
+
+ mutex_unlock(&pipe->lock);
+}
+
+static void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+
+ vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index), VI6_CMD_STRCMD);
+ pipe->state = VSP1_PIPELINE_RUNNING;
+ pipe->buffers_ready = 0;
+}
+
+static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED,
+ msecs_to_jiffies(500));
+ ret = ret == 0 ? -ETIMEDOUT : 0;
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ if (entity->route)
+ vsp1_write(entity->vsp1, entity->route,
+ VI6_DPR_NODE_UNUSED);
+
+ v4l2_subdev_call(&entity->subdev, video, s_stream, 0);
+ }
+
+ return ret;
+}
+
+static bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe)
+{
+ unsigned int mask;
+
+ mask = ((1 << pipe->num_inputs) - 1) << 1;
+ if (!pipe->lif)
+ mask |= 1 << 0;
+
+ return pipe->buffers_ready == mask;
+}
+
+/*
+ * vsp1_video_complete_buffer - Complete the current buffer
+ * @video: the video node
+ *
+ * This function completes the current buffer by filling its sequence number,
+ * time stamp and payload size, and hands it back to the videobuf core.
+ *
+ * Return the next queued buffer or NULL if the queue is empty.
+ */
+static struct vsp1_video_buffer *
+vsp1_video_complete_buffer(struct vsp1_video *video)
+{
+ struct vsp1_video_buffer *next = NULL;
+ struct vsp1_video_buffer *done;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+
+ if (list_empty(&video->irqqueue)) {
+ spin_unlock_irqrestore(&video->irqlock, flags);
+ return NULL;
+ }
+
+ done = list_first_entry(&video->irqqueue,
+ struct vsp1_video_buffer, queue);
+ list_del(&done->queue);
+
+ if (!list_empty(&video->irqqueue))
+ next = list_first_entry(&video->irqqueue,
+ struct vsp1_video_buffer, queue);
+
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ done->buf.v4l2_buf.sequence = video->sequence++;
+ v4l2_get_timestamp(&done->buf.v4l2_buf.timestamp);
+ for (i = 0; i < done->buf.num_planes; ++i)
+ vb2_set_plane_payload(&done->buf, i, done->length[i]);
+ vb2_buffer_done(&done->buf, VB2_BUF_STATE_DONE);
+
+ return next;
+}
+
+static void vsp1_video_frame_end(struct vsp1_pipeline *pipe,
+ struct vsp1_video *video)
+{
+ struct vsp1_video_buffer *buf;
+ unsigned long flags;
+
+ buf = vsp1_video_complete_buffer(video);
+ if (buf == NULL)
+ return;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ video->ops->queue(video, buf);
+ pipe->buffers_ready |= 1 << video->pipe_index;
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ if (pipe == NULL)
+ return;
+
+ /* Complete buffers on all video nodes. */
+ for (i = 0; i < pipe->num_inputs; ++i)
+ vsp1_video_frame_end(pipe, &pipe->inputs[i]->video);
+
+ if (!pipe->lif)
+ vsp1_video_frame_end(pipe, &pipe->output->video);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ /* If a stop has been requested, mark the pipeline as stopped and
+ * return.
+ */
+ if (pipe->state == VSP1_PIPELINE_STOPPING) {
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ wake_up(&pipe->wq);
+ goto done;
+ }
+
+ /* Restart the pipeline if ready. */
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+
+done:
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * videobuf2 Queue Operations
+ */
+
+static int
+vsp1_video_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ const struct v4l2_pix_format_mplane *format;
+ struct v4l2_pix_format_mplane pix_mp;
+ unsigned int i;
+
+ if (fmt) {
+ /* Make sure the format is valid and adjust the sizeimage field
+ * if needed.
+ */
+ if (!vsp1_video_format_adjust(video, &fmt->fmt.pix_mp, &pix_mp))
+ return -EINVAL;
+
+ format = &pix_mp;
+ } else {
+ format = &video->format;
+ }
+
+ *nplanes = format->num_planes;
+
+ for (i = 0; i < format->num_planes; ++i) {
+ sizes[i] = format->plane_fmt[i].sizeimage;
+ alloc_ctxs[i] = video->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int vsp1_video_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb);
+ const struct v4l2_pix_format_mplane *format = &video->format;
+ unsigned int i;
+
+ if (vb->num_planes < format->num_planes)
+ return -EINVAL;
+
+ buf->video = video;
+
+ for (i = 0; i < vb->num_planes; ++i) {
+ buf->addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ buf->length[i] = vb2_plane_size(vb, i);
+
+ if (buf->length[i] < format->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_video_buffer *buf = to_vsp1_video_buffer(vb);
+ unsigned long flags;
+ bool empty;
+
+ spin_lock_irqsave(&video->irqlock, flags);
+ empty = list_empty(&video->irqqueue);
+ list_add_tail(&buf->queue, &video->irqqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ if (!empty)
+ return;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+
+ video->ops->queue(video, buf);
+ pipe->buffers_ready |= 1 << video->pipe_index;
+
+ if (vb2_is_streaming(&video->queue) &&
+ vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+}
+
+static void vsp1_entity_route_setup(struct vsp1_entity *source)
+{
+ struct vsp1_entity *sink;
+
+ if (source->route == 0)
+ return;
+
+ sink = container_of(source->sink, struct vsp1_entity, subdev.entity);
+ vsp1_write(source->vsp1, source->route, sink->id);
+}
+
+static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ struct vsp1_entity *entity;
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&pipe->lock);
+ if (pipe->stream_count == pipe->num_video - 1) {
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ vsp1_entity_route_setup(entity);
+
+ ret = v4l2_subdev_call(&entity->subdev, video,
+ s_stream, 1);
+ if (ret < 0) {
+ mutex_unlock(&pipe->lock);
+ return ret;
+ }
+ }
+ }
+
+ pipe->stream_count++;
+ mutex_unlock(&pipe->lock);
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return 0;
+}
+
+static int vsp1_video_stop_streaming(struct vb2_queue *vq)
+{
+ struct vsp1_video *video = vb2_get_drv_priv(vq);
+ struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity);
+ unsigned long flags;
+ int ret;
+
+ mutex_lock(&pipe->lock);
+ if (--pipe->stream_count == 0) {
+ /* Stop the pipeline. */
+ ret = vsp1_pipeline_stop(pipe);
+ if (ret == -ETIMEDOUT)
+ dev_err(video->vsp1->dev, "pipeline stop timeout\n");
+ }
+ mutex_unlock(&pipe->lock);
+
+ vsp1_pipeline_cleanup(pipe);
+ media_entity_pipeline_stop(&video->video.entity);
+
+ /* Remove all buffers from the IRQ queue. */
+ spin_lock_irqsave(&video->irqlock, flags);
+ INIT_LIST_HEAD(&video->irqqueue);
+ spin_unlock_irqrestore(&video->irqlock, flags);
+
+ return 0;
+}
+
+static struct vb2_ops vsp1_video_queue_qops = {
+ .queue_setup = vsp1_video_queue_setup,
+ .buf_prepare = vsp1_video_buffer_prepare,
+ .buf_queue = vsp1_video_buffer_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+ .start_streaming = vsp1_video_start_streaming,
+ .stop_streaming = vsp1_video_stop_streaming,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 ioctls
+ */
+
+static int
+vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
+ | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_STREAMING;
+ else
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE
+ | V4L2_CAP_STREAMING;
+
+ strlcpy(cap->driver, "vsp1", sizeof(cap->driver));
+ strlcpy(cap->card, video->video.name, sizeof(cap->card));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(video->vsp1->dev));
+
+ return 0;
+}
+
+static int
+vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ mutex_lock(&video->lock);
+ format->fmt.pix_mp = video->format;
+ mutex_unlock(&video->lock);
+
+ return 0;
+}
+
+static int
+vsp1_video_try_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ return __vsp1_video_try_format(video, &format->fmt.pix_mp, NULL);
+}
+
+static int
+vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ const struct vsp1_format_info *info;
+ int ret;
+
+ if (format->type != video->queue.type)
+ return -EINVAL;
+
+ ret = __vsp1_video_try_format(video, &format->fmt.pix_mp, &info);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&video->lock);
+
+ if (vb2_is_busy(&video->queue)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ video->format = format->fmt.pix_mp;
+ video->fmtinfo = info;
+
+done:
+ mutex_unlock(&video->lock);
+ return ret;
+}
+
+static int
+vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ struct vsp1_pipeline *pipe;
+ int ret;
+
+ if (video->queue.owner && video->queue.owner != file->private_data)
+ return -EBUSY;
+
+ video->sequence = 0;
+
+ /* Start streaming on the pipeline. No link touching an entity in the
+ * pipeline can be activated or deactivated once streaming is started.
+ *
+ * Use the VSP1 pipeline object embedded in the first video object that
+ * starts streaming.
+ */
+ pipe = video->video.entity.pipe
+ ? to_vsp1_pipeline(&video->video.entity) : &video->pipe;
+
+ ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe);
+ if (ret < 0)
+ return ret;
+
+ /* Verify that the configured format matches the output of the connected
+ * subdev.
+ */
+ ret = vsp1_video_verify_format(video);
+ if (ret < 0)
+ goto err_stop;
+
+ ret = vsp1_pipeline_init(pipe, video);
+ if (ret < 0)
+ goto err_stop;
+
+ /* Start the queue. */
+ ret = vb2_streamon(&video->queue, type);
+ if (ret < 0)
+ goto err_cleanup;
+
+ return 0;
+
+err_cleanup:
+ vsp1_pipeline_cleanup(pipe);
+err_stop:
+ media_entity_pipeline_stop(&video->video.entity);
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
+ .vidioc_querycap = vsp1_video_querycap,
+ .vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format,
+ .vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format,
+ .vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format,
+ .vidioc_g_fmt_vid_out_mplane = vsp1_video_get_format,
+ .vidioc_s_fmt_vid_out_mplane = vsp1_video_set_format,
+ .vidioc_try_fmt_vid_out_mplane = vsp1_video_try_format,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vsp1_video_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 File Operations
+ */
+
+static int vsp1_video_open(struct file *file)
+{
+ struct vsp1_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh;
+ int ret = 0;
+
+ vfh = kzalloc(sizeof(*vfh), GFP_KERNEL);
+ if (vfh == NULL)
+ return -ENOMEM;
+
+ v4l2_fh_init(vfh, &video->video);
+ v4l2_fh_add(vfh);
+
+ file->private_data = vfh;
+
+ if (!vsp1_device_get(video->vsp1)) {
+ ret = -EBUSY;
+ v4l2_fh_del(vfh);
+ kfree(vfh);
+ }
+
+ return ret;
+}
+
+static int vsp1_video_release(struct file *file)
+{
+ struct vsp1_video *video = video_drvdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+
+ mutex_lock(&video->lock);
+ if (video->queue.owner == vfh) {
+ vb2_queue_release(&video->queue);
+ video->queue.owner = NULL;
+ }
+ mutex_unlock(&video->lock);
+
+ vsp1_device_put(video->vsp1);
+
+ v4l2_fh_release(file);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static struct v4l2_file_operations vsp1_video_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = video_ioctl2,
+ .open = vsp1_video_open,
+ .release = vsp1_video_release,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf)
+{
+ const char *direction;
+ int ret;
+
+ switch (video->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ direction = "output";
+ video->pad.flags = MEDIA_PAD_FL_SINK;
+ break;
+
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ direction = "input";
+ video->pad.flags = MEDIA_PAD_FL_SOURCE;
+ video->video.vfl_dir = VFL_DIR_TX;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ video->rwpf = rwpf;
+
+ mutex_init(&video->lock);
+ spin_lock_init(&video->irqlock);
+ INIT_LIST_HEAD(&video->irqqueue);
+
+ mutex_init(&video->pipe.lock);
+ spin_lock_init(&video->pipe.irqlock);
+ INIT_LIST_HEAD(&video->pipe.entities);
+ init_waitqueue_head(&video->pipe.wq);
+ video->pipe.state = VSP1_PIPELINE_STOPPED;
+
+ /* Initialize the media entity... */
+ ret = media_entity_init(&video->video.entity, 1, &video->pad, 0);
+ if (ret < 0)
+ return ret;
+
+ /* ... and the format ... */
+ video->fmtinfo = vsp1_get_format_info(VSP1_VIDEO_DEF_FORMAT);
+ video->format.pixelformat = video->fmtinfo->fourcc;
+ video->format.colorspace = V4L2_COLORSPACE_SRGB;
+ video->format.field = V4L2_FIELD_NONE;
+ video->format.width = VSP1_VIDEO_DEF_WIDTH;
+ video->format.height = VSP1_VIDEO_DEF_HEIGHT;
+ video->format.num_planes = 1;
+ video->format.plane_fmt[0].bytesperline =
+ video->format.width * video->fmtinfo->bpp[0] / 8;
+ video->format.plane_fmt[0].sizeimage =
+ video->format.plane_fmt[0].bytesperline * video->format.height;
+
+ /* ... and the video node... */
+ video->video.v4l2_dev = &video->vsp1->v4l2_dev;
+ video->video.fops = &vsp1_video_fops;
+ snprintf(video->video.name, sizeof(video->video.name), "%s %s",
+ rwpf->subdev.name, direction);
+ video->video.vfl_type = VFL_TYPE_GRABBER;
+ video->video.release = video_device_release_empty;
+ video->video.ioctl_ops = &vsp1_video_ioctl_ops;
+
+ video_set_drvdata(&video->video, video);
+
+ /* ... and the buffers queue... */
+ video->alloc_ctx = vb2_dma_contig_init_ctx(video->vsp1->dev);
+ if (IS_ERR(video->alloc_ctx))
+ goto error;
+
+ video->queue.type = video->type;
+ video->queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
+ video->queue.lock = &video->lock;
+ video->queue.drv_priv = video;
+ video->queue.buf_struct_size = sizeof(struct vsp1_video_buffer);
+ video->queue.ops = &vsp1_video_queue_qops;
+ video->queue.mem_ops = &vb2_dma_contig_memops;
+ video->queue.timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ ret = vb2_queue_init(&video->queue);
+ if (ret < 0) {
+ dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n");
+ goto error;
+ }
+
+ /* ... and register the video device. */
+ video->video.queue = &video->queue;
+ ret = video_register_device(&video->video, VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ dev_err(video->vsp1->dev, "failed to register video device\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ vsp1_video_cleanup(video);
+ return ret;
+}
+
+void vsp1_video_cleanup(struct vsp1_video *video)
+{
+ if (video_is_registered(&video->video))
+ video_unregister_device(&video->video);
+
+ vb2_dma_contig_cleanup_ctx(video->alloc_ctx);
+ media_entity_cleanup(&video->video.entity);
+}
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
new file mode 100644
index 000000000000..d8612a378345
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -0,0 +1,144 @@
+/*
+ * vsp1_video.h -- R-Car VSP1 Video Node
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __VSP1_VIDEO_H__
+#define __VSP1_VIDEO_H__
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <media/media-entity.h>
+#include <media/videobuf2-core.h>
+
+struct vsp1_video;
+
+/*
+ * struct vsp1_format_info - VSP1 video format description
+ * @mbus: media bus format code
+ * @fourcc: V4L2 pixel format FCC identifier
+ * @planes: number of planes
+ * @bpp: bits per pixel
+ * @hwfmt: VSP1 hardware format
+ * @swap_yc: the Y and C components are swapped (Y comes before C)
+ * @swap_uv: the U and V components are swapped (V comes before U)
+ * @hsub: horizontal subsampling factor
+ * @vsub: vertical subsampling factor
+ */
+struct vsp1_format_info {
+ u32 fourcc;
+ unsigned int mbus;
+ unsigned int hwfmt;
+ unsigned int swap;
+ unsigned int planes;
+ unsigned int bpp[3];
+ bool swap_yc;
+ bool swap_uv;
+ unsigned int hsub;
+ unsigned int vsub;
+};
+
+enum vsp1_pipeline_state {
+ VSP1_PIPELINE_STOPPED,
+ VSP1_PIPELINE_RUNNING,
+ VSP1_PIPELINE_STOPPING,
+};
+
+/*
+ * struct vsp1_pipeline - A VSP1 hardware pipeline
+ * @media: the media pipeline
+ * @irqlock: protects the pipeline state
+ * @lock: protects the pipeline use count and stream count
+ */
+struct vsp1_pipeline {
+ struct media_pipeline pipe;
+
+ spinlock_t irqlock;
+ enum vsp1_pipeline_state state;
+ wait_queue_head_t wq;
+
+ struct mutex lock;
+ unsigned int use_count;
+ unsigned int stream_count;
+ unsigned int buffers_ready;
+
+ unsigned int num_video;
+ unsigned int num_inputs;
+ struct vsp1_rwpf *inputs[VPS1_MAX_RPF];
+ struct vsp1_rwpf *output;
+ struct vsp1_entity *lif;
+
+ struct list_head entities;
+};
+
+static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
+{
+ if (likely(e->pipe))
+ return container_of(e->pipe, struct vsp1_pipeline, pipe);
+ else
+ return NULL;
+}
+
+struct vsp1_video_buffer {
+ struct vsp1_video *video;
+ struct vb2_buffer buf;
+ struct list_head queue;
+
+ dma_addr_t addr[3];
+ unsigned int length[3];
+};
+
+static inline struct vsp1_video_buffer *
+to_vsp1_video_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct vsp1_video_buffer, buf);
+}
+
+struct vsp1_video_operations {
+ void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf);
+};
+
+struct vsp1_video {
+ struct vsp1_device *vsp1;
+ struct vsp1_entity *rwpf;
+
+ const struct vsp1_video_operations *ops;
+
+ struct video_device video;
+ enum v4l2_buf_type type;
+ struct media_pad pad;
+
+ struct mutex lock;
+ struct v4l2_pix_format_mplane format;
+ const struct vsp1_format_info *fmtinfo;
+
+ struct vsp1_pipeline pipe;
+ unsigned int pipe_index;
+
+ struct vb2_queue queue;
+ void *alloc_ctx;
+ spinlock_t irqlock;
+ struct list_head irqqueue;
+ unsigned int sequence;
+};
+
+static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
+{
+ return container_of(vdev, struct vsp1_video, video);
+}
+
+int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf);
+void vsp1_video_cleanup(struct vsp1_video *video);
+
+void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
+
+#endif /* __VSP1_VIDEO_H__ */
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c
new file mode 100644
index 000000000000..db4b85ee05fc
--- /dev/null
+++ b/drivers/media/platform/vsp1/vsp1_wpf.c
@@ -0,0 +1,233 @@
+/*
+ * vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
+ *
+ * Copyright (C) 2013 Renesas Corporation
+ *
+ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.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 by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/device.h>
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1.h"
+#include "vsp1_rwpf.h"
+#include "vsp1_video.h"
+
+#define WPF_MAX_WIDTH 2048
+#define WPF_MAX_HEIGHT 2048
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg)
+{
+ return vsp1_read(wpf->entity.vsp1,
+ reg + wpf->entity.index * VI6_WPF_OFFSET);
+}
+
+static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data)
+{
+ vsp1_write(wpf->entity.vsp1,
+ reg + wpf->entity.index * VI6_WPF_OFFSET, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Core Operations
+ */
+
+static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
+{
+ struct vsp1_rwpf *wpf = to_rwpf(subdev);
+ struct vsp1_pipeline *pipe =
+ to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ struct vsp1_device *vsp1 = wpf->entity.vsp1;
+ const struct v4l2_mbus_framefmt *format =
+ &wpf->entity.formats[RWPF_PAD_SOURCE];
+ unsigned int i;
+ u32 srcrpf = 0;
+ u32 outfmt = 0;
+
+ if (!enable) {
+ vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
+ return 0;
+ }
+
+ /* Sources */
+ for (i = 0; i < pipe->num_inputs; ++i) {
+ struct vsp1_rwpf *input = pipe->inputs[i];
+
+ srcrpf |= VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index);
+ }
+
+ vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf);
+
+ /* Destination stride. Cropping isn't supported yet. */
+ if (!pipe->lif) {
+ struct v4l2_pix_format_mplane *format = &wpf->video.format;
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y,
+ format->plane_fmt[0].bytesperline);
+ if (format->num_planes > 1)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C,
+ format->plane_fmt[1].bytesperline);
+ }
+
+ vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP,
+ format->width << VI6_WPF_SZCLIP_SIZE_SHIFT);
+ vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP,
+ format->height << VI6_WPF_SZCLIP_SIZE_SHIFT);
+
+ /* Format */
+ if (!pipe->lif) {
+ const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo;
+
+ outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
+
+ if (fmtinfo->swap_yc)
+ outfmt |= VI6_WPF_OUTFMT_SPYCS;
+ if (fmtinfo->swap_uv)
+ outfmt |= VI6_WPF_OUTFMT_SPUVS;
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap);
+ }
+
+ if (wpf->entity.formats[RWPF_PAD_SINK].code !=
+ wpf->entity.formats[RWPF_PAD_SOURCE].code)
+ outfmt |= VI6_WPF_OUTFMT_CSC;
+
+ vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt);
+
+ vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index),
+ VI6_DPR_WPF_FPORCH_FP_WPFN);
+
+ vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0);
+
+ /* Enable interrupts */
+ vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
+ vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index),
+ VI6_WFP_IRQ_ENB_FREE);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static struct v4l2_subdev_video_ops wpf_video_ops = {
+ .s_stream = wpf_s_stream,
+};
+
+static struct v4l2_subdev_pad_ops wpf_pad_ops = {
+ .enum_mbus_code = vsp1_rwpf_enum_mbus_code,
+ .enum_frame_size = vsp1_rwpf_enum_frame_size,
+ .get_fmt = vsp1_rwpf_get_format,
+ .set_fmt = vsp1_rwpf_set_format,
+};
+
+static struct v4l2_subdev_ops wpf_ops = {
+ .video = &wpf_video_ops,
+ .pad = &wpf_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Video Device Operations
+ */
+
+static void wpf_vdev_queue(struct vsp1_video *video,
+ struct vsp1_video_buffer *buf)
+{
+ struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video);
+
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]);
+ if (buf->buf.num_planes > 1)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]);
+ if (buf->buf.num_planes > 2)
+ vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]);
+}
+
+static const struct vsp1_video_operations wpf_vdev_ops = {
+ .queue = wpf_vdev_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
+{
+ struct v4l2_subdev *subdev;
+ struct vsp1_video *video;
+ struct vsp1_rwpf *wpf;
+ unsigned int flags;
+ int ret;
+
+ wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
+ if (wpf == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ wpf->max_width = WPF_MAX_WIDTH;
+ wpf->max_height = WPF_MAX_HEIGHT;
+
+ wpf->entity.type = VSP1_ENTITY_WPF;
+ wpf->entity.index = index;
+ wpf->entity.id = VI6_DPR_NODE_WPF(index);
+
+ ret = vsp1_entity_init(vsp1, &wpf->entity, 2);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ /* Initialize the V4L2 subdev. */
+ subdev = &wpf->entity.subdev;
+ v4l2_subdev_init(subdev, &wpf_ops);
+
+ subdev->entity.ops = &vsp1_media_ops;
+ subdev->internal_ops = &vsp1_subdev_internal_ops;
+ snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u",
+ dev_name(vsp1->dev), index);
+ v4l2_set_subdevdata(subdev, wpf);
+ subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ vsp1_entity_init_formats(subdev, NULL);
+
+ /* Initialize the video device. */
+ video = &wpf->video;
+
+ video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ video->vsp1 = vsp1;
+ video->ops = &wpf_vdev_ops;
+
+ ret = vsp1_video_init(video, &wpf->entity);
+ if (ret < 0)
+ goto error_video;
+
+ /* Connect the video device to the WPF. All connections are immutable
+ * except for the WPF0 source link if a LIF is present.
+ */
+ flags = MEDIA_LNK_FL_ENABLED;
+ if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0)
+ flags |= MEDIA_LNK_FL_IMMUTABLE;
+
+ ret = media_entity_create_link(&wpf->entity.subdev.entity,
+ RWPF_PAD_SOURCE,
+ &wpf->video.video.entity, 0, flags);
+ if (ret < 0)
+ goto error_link;
+
+ wpf->entity.sink = &wpf->video.video.entity;
+
+ return wpf;
+
+error_link:
+ vsp1_video_cleanup(video);
+error_video:
+ media_entity_cleanup(&wpf->entity.subdev.entity);
+ return ERR_PTR(ret);
+}