summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig27
-rw-r--r--drivers/media/platform/Makefile2
-rw-r--r--drivers/media/platform/coda/Makefile2
-rw-r--r--drivers/media/platform/coda/coda-bit.c147
-rw-r--r--drivers/media/platform/coda/coda-common.c338
-rw-r--r--drivers/media/platform/coda/coda-gdi.c150
-rw-r--r--drivers/media/platform/coda/coda.h15
-rw-r--r--drivers/media/platform/coda/coda_regs.h10
-rw-r--r--drivers/media/platform/coda/trace.h89
-rw-r--r--drivers/media/platform/exynos4-is/fimc-m2m.c2
-rw-r--r--drivers/media/platform/fsl-viu.c160
-rw-r--r--drivers/media/platform/omap/Kconfig1
-rw-r--r--drivers/media/platform/omap/omap_vout.c71
-rw-r--r--drivers/media/platform/omap3isp/isp.c133
-rw-r--r--drivers/media/platform/omap3isp/isp.h7
-rw-r--r--drivers/media/platform/omap3isp/ispcsiphy.h2
-rw-r--r--drivers/media/platform/omap3isp/ispvideo.c9
-rw-r--r--drivers/media/platform/omap3isp/omap3isp.h132
-rw-r--r--drivers/media/platform/rcar_jpu.c1794
-rw-r--r--drivers/media/platform/s5p-jpeg/jpeg-core.c14
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c6
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_enc.c9
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.c11
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr.h2
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c12
-rw-r--r--drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c10
-rw-r--r--drivers/media/platform/s5p-tv/hdmiphy_drv.c1
-rw-r--r--drivers/media/platform/s5p-tv/mixer_reg.c12
-rw-r--r--drivers/media/platform/s5p-tv/sii9234_drv.c1
-rw-r--r--drivers/media/platform/sh_veu.c10
-rw-r--r--drivers/media/platform/sh_vou.c817
-rw-r--r--drivers/media/platform/soc_camera/atmel-isi.c105
-rw-r--r--drivers/media/platform/soc_camera/rcar_vin.c16
-rw-r--r--drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c3
-rw-r--r--drivers/media/platform/soc_camera/soc_camera.c48
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-debug.c8
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-hw.c12
-rw-r--r--drivers/media/platform/sti/bdisp/bdisp-v4l2.c76
-rw-r--r--drivers/media/platform/sti/c8sectpfe/Kconfig28
-rw-r--r--drivers/media/platform/sti/c8sectpfe/Makefile9
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c265
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h64
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c1235
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h288
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c271
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h26
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c244
-rw-r--r--drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h20
-rw-r--r--drivers/media/platform/vivid/vivid-vid-cap.c2
-rw-r--r--drivers/media/platform/vivid/vivid-vid-out.c15
-rw-r--r--drivers/media/platform/vsp1/vsp1_drv.c13
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.c18
-rw-r--r--drivers/media/platform/vsp1/vsp1_entity.h4
-rw-r--r--drivers/media/platform/vsp1/vsp1_regs.h6
-rw-r--r--drivers/media/platform/vsp1/vsp1_rwpf.c11
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.c85
-rw-r--r--drivers/media/platform/vsp1/vsp1_video.h5
57 files changed, 5705 insertions, 1168 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index f6bed1971..dc75694ac 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -1,6 +1,6 @@
#
# Platform drivers
-# All drivers here are currently for webcam support
+# Most drivers here are currently for webcam support
menuconfig V4L_PLATFORM_DRIVERS
bool "V4L platform devices"
@@ -86,7 +86,7 @@ config VIDEO_M32R_AR_M64278
config VIDEO_OMAP3
tristate "OMAP 3 Camera support"
depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && ARCH_OMAP3
- depends on HAS_DMA
+ depends on HAS_DMA && OF
depends on OMAP_IOMMU
select ARM_DMA_USE_IOMMU
select VIDEOBUF2_DMA_CONTIG
@@ -231,6 +231,18 @@ config VIDEO_SH_VEU
Support for the Video Engine Unit (VEU) on SuperH and
SH-Mobile SoCs.
+config VIDEO_RENESAS_JPU
+ tristate "Renesas JPEG Processing Unit"
+ depends on VIDEO_DEV && VIDEO_V4L2
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ ---help---
+ This is a V4L2 driver for the Renesas JPEG Processing Unit.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rcar_jpu.
+
config VIDEO_RENESAS_VSP1
tristate "Renesas VSP1 Video Processing Engine"
depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA
@@ -280,3 +292,14 @@ config VIDEO_VIM2M
This is a virtual test device for the memory-to-memory driver
framework.
endif #V4L_TEST_DRIVERS
+
+menuconfig DVB_PLATFORM_DRIVERS
+ bool "DVB platform devices"
+ depends on MEDIA_DIGITAL_TV_SUPPORT
+ default n
+ ---help---
+ Say Y here to enable support for platform-specific Digital TV drivers.
+
+if DVB_PLATFORM_DRIVERS
+source "drivers/media/platform/sti/c8sectpfe/Kconfig"
+endif #DVB_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 114f9aba1..efa0295af 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/
obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/
+obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/
obj-$(CONFIG_BLACKFIN) += blackfin/
@@ -44,6 +45,7 @@ obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
obj-$(CONFIG_SOC_CAMERA) += soc_camera/
+obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
obj-y += omap/
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/coda/Makefile
index 834e504bf..9342ac57b 100644
--- a/drivers/media/platform/coda/Makefile
+++ b/drivers/media/platform/coda/Makefile
@@ -1,5 +1,5 @@
ccflags-y += -I$(src)
-coda-objs := coda-common.o coda-bit.o coda-h264.o coda-jpeg.o
+coda-objs := coda-common.o coda-bit.o coda-gdi.o coda-h264.o coda-jpeg.o
obj-$(CONFIG_VIDEO_CODA) += coda.o
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c
index 109797bb8..fd7819d89 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/coda/coda-bit.c
@@ -226,8 +226,12 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
{
struct vb2_buffer *src_buf;
struct coda_buffer_meta *meta;
+ unsigned long flags;
u32 start;
+ if (ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG)
+ return;
+
while (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) > 0) {
/*
* Only queue a single JPEG into the bitstream buffer, except
@@ -252,6 +256,13 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
continue;
}
+ /* Dump empty buffers */
+ if (!vb2_get_plane_payload(src_buf, 0)) {
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ continue;
+ }
+
/* Buffer start position */
start = ctx->bitstream_fifo.kfifo.in &
ctx->bitstream_fifo.kfifo.mask;
@@ -271,8 +282,13 @@ void coda_fill_bitstream(struct coda_ctx *ctx, bool streaming)
meta->start = start;
meta->end = ctx->bitstream_fifo.kfifo.in &
ctx->bitstream_fifo.kfifo.mask;
+ spin_lock_irqsave(&ctx->buffer_meta_lock,
+ flags);
list_add_tail(&meta->list,
&ctx->buffer_meta_list);
+ ctx->num_metas++;
+ spin_unlock_irqrestore(&ctx->buffer_meta_lock,
+ flags);
trace_coda_bit_queue(ctx, src_buf, meta);
}
@@ -331,7 +347,6 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
{
struct coda_dev *dev = ctx->dev;
int width, height;
- dma_addr_t paddr;
int ysize;
int ret;
int i;
@@ -351,7 +366,10 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
size_t size;
char *name;
- size = ysize + ysize / 2;
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
+ size = round_up(ysize, 4096) + ysize / 2;
+ else
+ size = ysize + ysize / 2;
if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
dev->devtype->product != CODA_DX6)
size += ysize / 4;
@@ -367,11 +385,23 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
/* Register frame buffers in the parameter buffer */
for (i = 0; i < ctx->num_internal_frames; i++) {
- paddr = ctx->internal_frames[i].paddr;
+ u32 y, cb, cr;
+
/* Start addresses of Y, Cb, Cr planes */
- coda_parabuf_write(ctx, i * 3 + 0, paddr);
- coda_parabuf_write(ctx, i * 3 + 1, paddr + ysize);
- coda_parabuf_write(ctx, i * 3 + 2, paddr + ysize + ysize / 4);
+ y = ctx->internal_frames[i].paddr;
+ cb = y + ysize;
+ cr = y + ysize + ysize/4;
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP) {
+ cb = round_up(cb, 4096);
+ cr = 0;
+ /* Packed 20-bit MSB of base addresses */
+ /* YYYYYCCC, CCyyyyyc, cccc.... */
+ y = (y & 0xfffff000) | cb >> 20;
+ cb = (cb & 0x000ff000) << 12;
+ }
+ coda_parabuf_write(ctx, i * 3 + 0, y);
+ coda_parabuf_write(ctx, i * 3 + 1, cb);
+ coda_parabuf_write(ctx, i * 3 + 2, cr);
/* mvcol buffer for h.264 */
if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 &&
@@ -384,7 +414,7 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx,
/* 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 +
+ coda_parabuf_write(ctx, 97, ctx->internal_frames[0].paddr +
ysize + ysize/4 + ysize/4);
return 0;
@@ -712,6 +742,32 @@ err_clk_per:
return ret;
}
+static void coda9_set_frame_cache(struct coda_ctx *ctx, u32 fourcc)
+{
+ u32 cache_size, cache_config;
+
+ if (ctx->tiled_map_type == GDI_LINEAR_FRAME_MAP) {
+ /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */
+ cache_size = 0x20262024;
+ cache_config = 2 << CODA9_CACHE_PAGEMERGE_OFFSET;
+ } else {
+ /* Luma 0x2 page, 4x4 cache, chroma 0x2 page, 4x3 cache size */
+ cache_size = 0x02440243;
+ cache_config = 1 << CODA9_CACHE_PAGEMERGE_OFFSET;
+ }
+ coda_write(ctx->dev, cache_size, CODA9_CMD_SET_FRAME_CACHE_SIZE);
+ if (fourcc == V4L2_PIX_FMT_NV12) {
+ cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
+ 16 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET |
+ 0 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET;
+ } else {
+ cache_config |= 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
+ 8 << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET |
+ 8 << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET;
+ }
+ coda_write(ctx->dev, cache_config, CODA9_CMD_SET_FRAME_CACHE_CONFIG);
+}
+
/*
* Encoder context operations
*/
@@ -789,9 +845,12 @@ static int coda_start_encoding(struct coda_ctx *ctx)
break;
}
- ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE;
+ ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) |
+ CODA9_FRAME_TILED2LINEAR);
if (q_data_src->fourcc == V4L2_PIX_FMT_NV12)
ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
+ ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR;
coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
if (dev->devtype->product == CODA_DX6) {
@@ -913,6 +972,9 @@ static int coda_start_encoding(struct coda_ctx *ctx)
value = (ctx->params.bitrate & CODA_RATECONTROL_BITRATE_MASK)
<< CODA_RATECONTROL_BITRATE_OFFSET;
value |= 1 & CODA_RATECONTROL_ENABLE_MASK;
+ value |= (ctx->params.vbv_delay &
+ CODA_RATECONTROL_INITIALDELAY_MASK)
+ << CODA_RATECONTROL_INITIALDELAY_OFFSET;
if (dev->devtype->product == CODA_960)
value |= BIT(31); /* disable autoskip */
} else {
@@ -920,7 +982,7 @@ static int coda_start_encoding(struct coda_ctx *ctx)
}
coda_write(dev, value, CODA_CMD_ENC_SEQ_RC_PARA);
- coda_write(dev, 0, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
+ coda_write(dev, ctx->params.vbv_size, CODA_CMD_ENC_SEQ_RC_BUF_SIZE);
coda_write(dev, ctx->params.intra_refresh,
CODA_CMD_ENC_SEQ_INTRA_REFRESH);
@@ -996,6 +1058,7 @@ static int coda_start_encoding(struct coda_ctx *ctx)
ret = -EFAULT;
goto out;
}
+ ctx->initialized = 1;
if (dst_fourcc != V4L2_PIX_FMT_JPEG) {
if (dev->devtype->product == CODA_960)
@@ -1036,6 +1099,8 @@ static int coda_start_encoding(struct coda_ctx *ctx)
coda_write(dev, ctx->iram_info.buf_btp_use,
CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
+ coda9_set_frame_cache(ctx, q_data_src->fourcc);
+
/* FIXME */
coda_write(dev, ctx->internal_frames[2].paddr,
CODA9_CMD_SET_FRAME_SUBSAMP_A);
@@ -1326,6 +1391,9 @@ static void coda_seq_end_work(struct work_struct *work)
mutex_lock(&ctx->buffer_mutex);
mutex_lock(&dev->coda_mutex);
+ if (ctx->initialized == 0)
+ goto out;
+
v4l2_dbg(1, coda_debug, &dev->v4l2_dev,
"%d: %s: sent command 'SEQ_END' to coda\n", ctx->idx,
__func__);
@@ -1334,11 +1402,22 @@ static void coda_seq_end_work(struct work_struct *work)
"CODA_COMMAND_SEQ_END failed\n");
}
+ /*
+ * FIXME: Sometimes h.264 encoding fails with 8-byte sequences missing
+ * from the output stream after the h.264 decoder has run. Resetting the
+ * hardware after the decoder has finished seems to help.
+ */
+ if (dev->devtype->product == CODA_960)
+ coda_hw_reset(ctx);
+
kfifo_init(&ctx->bitstream_fifo,
ctx->bitstream.vaddr, ctx->bitstream.size);
coda_free_framebuffers(ctx);
+ ctx->initialized = 0;
+
+out:
mutex_unlock(&dev->coda_mutex);
mutex_unlock(&ctx->buffer_mutex);
}
@@ -1448,9 +1527,12 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
/* Update coda bitstream read and write pointers from kfifo */
coda_kfifo_sync_to_device_full(ctx);
- ctx->frame_mem_ctrl &= ~CODA_FRAME_CHROMA_INTERLEAVE;
+ ctx->frame_mem_ctrl &= ~(CODA_FRAME_CHROMA_INTERLEAVE | (0x3 << 9) |
+ CODA9_FRAME_TILED2LINEAR);
if (dst_fourcc == V4L2_PIX_FMT_NV12)
ctx->frame_mem_ctrl |= CODA_FRAME_CHROMA_INTERLEAVE;
+ if (ctx->tiled_map_type == GDI_TILED_FRAME_MB_RASTER_MAP)
+ ctx->frame_mem_ctrl |= (0x3 << 9) | CODA9_FRAME_TILED2LINEAR;
coda_write(dev, ctx->frame_mem_ctrl, CODA_REG_BIT_FRAME_MEM_CTRL);
ctx->display_idx = -1;
@@ -1496,6 +1578,7 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
coda_write(dev, 0, CODA_REG_BIT_BIT_STREAM_PARAM);
return -ETIMEDOUT;
}
+ ctx->initialized = 1;
/* Update kfifo out pointer from coda bitstream read pointer */
coda_kfifo_sync_from_device(ctx);
@@ -1578,30 +1661,13 @@ static int __coda_start_decoding(struct coda_ctx *ctx)
CODA7_CMD_SET_FRAME_AXI_DBKC_ADDR);
coda_write(dev, ctx->iram_info.buf_ovl_use,
CODA7_CMD_SET_FRAME_AXI_OVL_ADDR);
- if (dev->devtype->product == CODA_960)
+ if (dev->devtype->product == CODA_960) {
coda_write(dev, ctx->iram_info.buf_btp_use,
CODA9_CMD_SET_FRAME_AXI_BTP_ADDR);
- }
- if (dev->devtype->product == CODA_960) {
- int cbb_size, crb_size;
-
- coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
- /* Luma 2x0 page, 2x6 cache, chroma 2x0 page, 2x4 cache size */
- coda_write(dev, 0x20262024, CODA9_CMD_SET_FRAME_CACHE_SIZE);
-
- if (dst_fourcc == V4L2_PIX_FMT_NV12) {
- cbb_size = 0;
- crb_size = 16;
- } else {
- cbb_size = 8;
- crb_size = 8;
+ coda_write(dev, -1, CODA9_CMD_SET_FRAME_DELAY);
+ coda9_set_frame_cache(ctx, dst_fourcc);
}
- coda_write(dev, 2 << CODA9_CACHE_PAGEMERGE_OFFSET |
- 32 << CODA9_CACHE_LUMA_BUFFER_SIZE_OFFSET |
- cbb_size << CODA9_CACHE_CB_BUFFER_SIZE_OFFSET |
- crb_size << CODA9_CACHE_CR_BUFFER_SIZE_OFFSET,
- CODA9_CMD_SET_FRAME_CACHE_CONFIG);
}
if (src_fourcc == V4L2_PIX_FMT_H264) {
@@ -1654,6 +1720,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
struct coda_dev *dev = ctx->dev;
struct coda_q_data *q_data_dst;
struct coda_buffer_meta *meta;
+ unsigned long flags;
u32 reg_addr, reg_stride;
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
@@ -1732,6 +1799,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
coda_write(dev, ctx->iram_info.axi_sram_use,
CODA7_REG_BIT_AXI_SRAM_USE);
+ spin_lock_irqsave(&ctx->buffer_meta_lock, flags);
meta = list_first_entry_or_null(&ctx->buffer_meta_list,
struct coda_buffer_meta, list);
@@ -1751,6 +1819,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx)
kfifo_in(&ctx->bitstream_fifo, buf, pad);
}
}
+ spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags);
coda_kfifo_sync_to_device_full(ctx);
@@ -1772,6 +1841,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
struct vb2_buffer *dst_buf;
struct coda_buffer_meta *meta;
unsigned long payload;
+ unsigned long flags;
int width, height;
int decoded_idx;
int display_idx;
@@ -1897,12 +1967,21 @@ static void coda_finish_decode(struct coda_ctx *ctx)
} else {
val = coda_read(dev, CODA_RET_DEC_PIC_FRAME_NUM) - 1;
val -= ctx->sequence_offset;
- mutex_lock(&ctx->bitstream_mutex);
+ spin_lock_irqsave(&ctx->buffer_meta_lock, flags);
if (!list_empty(&ctx->buffer_meta_list)) {
meta = list_first_entry(&ctx->buffer_meta_list,
struct coda_buffer_meta, list);
list_del(&meta->list);
- if (val != (meta->sequence & 0xffff)) {
+ ctx->num_metas--;
+ spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags);
+ /*
+ * Clamp counters to 16 bits for comparison, as the HW
+ * counter rolls over at this point for h.264. This
+ * may be different for other formats, but using 16 bits
+ * should be enough to detect most errors and saves us
+ * from doing different things based on the format.
+ */
+ if ((val & 0xffff) != (meta->sequence & 0xffff)) {
v4l2_err(&dev->v4l2_dev,
"sequence number mismatch (%d(%d) != %d)\n",
val, ctx->sequence_offset,
@@ -1911,13 +1990,13 @@ static void coda_finish_decode(struct coda_ctx *ctx)
ctx->frame_metas[decoded_idx] = *meta;
kfree(meta);
} else {
+ spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags);
v4l2_err(&dev->v4l2_dev, "empty timestamp list!\n");
memset(&ctx->frame_metas[decoded_idx], 0,
sizeof(struct coda_buffer_meta));
ctx->frame_metas[decoded_idx].sequence = val;
ctx->sequence_offset++;
}
- mutex_unlock(&ctx->bitstream_mutex);
trace_coda_dec_pic_done(ctx, &ctx->frame_metas[decoded_idx]);
@@ -1960,7 +2039,7 @@ static void coda_finish_decode(struct coda_ctx *ctx)
dst_buf->v4l2_buf.timecode = meta->timecode;
dst_buf->v4l2_buf.timestamp = meta->timestamp;
- trace_coda_dec_rot_done(ctx, meta, dst_buf);
+ trace_coda_dec_rot_done(ctx, dst_buf, meta);
switch (q_data_dst->fourcc) {
case V4L2_PIX_FMT_YUV420:
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c
index 6e41b0ce8..c48ef89f9 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/coda/coda-common.c
@@ -15,6 +15,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/firmware.h>
+#include <linux/gcd.h>
#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -61,10 +62,9 @@ int coda_debug;
module_param(coda_debug, int, 0644);
MODULE_PARM_DESC(coda_debug, "Debug level (0-2)");
-struct coda_fmt {
- char *name;
- u32 fourcc;
-};
+static int disable_tiling;
+module_param(disable_tiling, int, 0644);
+MODULE_PARM_DESC(disable_tiling, "Disable tiled frame buffers");
void coda_write(struct coda_dev *dev, u32 data, u32 reg)
{
@@ -90,17 +90,17 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
u32 base_cb, base_cr;
switch (q_data->fourcc) {
- case V4L2_PIX_FMT_YVU420:
- /* Switch Cb and Cr for YVU420 format */
- base_cr = base_y + q_data->bytesperline * q_data->height;
- base_cb = base_cr + q_data->bytesperline * q_data->height / 4;
- break;
- case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_YUV420:
default:
base_cb = base_y + q_data->bytesperline * q_data->height;
base_cr = base_cb + q_data->bytesperline * q_data->height / 4;
break;
+ case V4L2_PIX_FMT_YVU420:
+ /* Switch Cb and Cr for YVU420 format */
+ base_cr = base_y + q_data->bytesperline * q_data->height;
+ base_cb = base_cr + q_data->bytesperline * q_data->height / 4;
+ break;
case V4L2_PIX_FMT_YUV422P:
base_cb = base_y + q_data->bytesperline * q_data->height;
base_cr = base_cb + q_data->bytesperline * q_data->height / 2;
@@ -111,40 +111,6 @@ void coda_write_base(struct coda_ctx *ctx, struct coda_q_data *q_data,
coda_write(ctx->dev, base_cr, reg_y + 8);
}
-/*
- * Array of all formats supported by any version of Coda:
- */
-static const struct coda_fmt coda_formats[] = {
- {
- .name = "YUV 4:2:0 Planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- },
- {
- .name = "YUV 4:2:0 Planar, YCrCb",
- .fourcc = V4L2_PIX_FMT_YVU420,
- },
- {
- .name = "YUV 4:2:0 Partial interleaved Y/CbCr",
- .fourcc = V4L2_PIX_FMT_NV12,
- },
- {
- .name = "YUV 4:2:2 Planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV422P,
- },
- {
- .name = "H264 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_H264,
- },
- {
- .name = "MPEG4 Encoded Stream",
- .fourcc = V4L2_PIX_FMT_MPEG4,
- },
- {
- .name = "JPEG Encoded Images",
- .fourcc = V4L2_PIX_FMT_JPEG,
- },
-};
-
#define CODA_CODEC(mode, src_fourcc, dst_fourcc, max_w, max_h) \
{ mode, src_fourcc, dst_fourcc, max_w, max_h }
@@ -190,9 +156,9 @@ static const struct coda_video_device coda_bit_encoder = {
.type = CODA_INST_ENCODER,
.ops = &coda_bit_encode_ops,
.src_formats = {
+ V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
- V4L2_PIX_FMT_NV12,
},
.dst_formats = {
V4L2_PIX_FMT_H264,
@@ -205,9 +171,9 @@ static const struct coda_video_device coda_bit_jpeg_encoder = {
.type = CODA_INST_ENCODER,
.ops = &coda_bit_encode_ops,
.src_formats = {
+ V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
- V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV422P,
},
.dst_formats = {
@@ -224,9 +190,9 @@ static const struct coda_video_device coda_bit_decoder = {
V4L2_PIX_FMT_MPEG4,
},
.dst_formats = {
+ V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
- V4L2_PIX_FMT_NV12,
},
};
@@ -238,9 +204,9 @@ static const struct coda_video_device coda_bit_jpeg_decoder = {
V4L2_PIX_FMT_JPEG,
},
.dst_formats = {
+ V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV420,
V4L2_PIX_FMT_YVU420,
- V4L2_PIX_FMT_NV12,
V4L2_PIX_FMT_YUV422P,
},
};
@@ -261,38 +227,21 @@ static const struct coda_video_device *coda9_video_devices[] = {
&coda_bit_decoder,
};
-static bool coda_format_is_yuv(u32 fourcc)
+/*
+ * Normalize all supported YUV 4:2:0 formats to the value used in the codec
+ * tables.
+ */
+static u32 coda_format_normalize_yuv(u32 fourcc)
{
switch (fourcc) {
+ case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_YUV422P:
- return true;
+ return V4L2_PIX_FMT_YUV420;
default:
- return false;
- }
-}
-
-static const char *coda_format_name(u32 fourcc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(coda_formats); i++) {
- if (coda_formats[i].fourcc == fourcc)
- return coda_formats[i].name;
+ return fourcc;
}
-
- return NULL;
-}
-
-/*
- * Normalize all supported YUV 4:2:0 formats to the value used in the codec
- * tables.
- */
-static u32 coda_format_normalize_yuv(u32 fourcc)
-{
- return coda_format_is_yuv(fourcc) ? V4L2_PIX_FMT_YUV420 : fourcc;
}
static const struct coda_codec *coda_find_codec(struct coda_dev *dev,
@@ -396,7 +345,6 @@ static int coda_enum_fmt(struct file *file, void *priv,
struct video_device *vdev = video_devdata(file);
const struct coda_video_device *cvd = to_coda_video_device(vdev);
const u32 *formats;
- const char *name;
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
formats = cvd->src_formats;
@@ -408,11 +356,7 @@ static int coda_enum_fmt(struct file *file, void *priv,
if (f->index >= CODA_MAX_FORMATS || formats[f->index] == 0)
return -EINVAL;
- name = coda_format_name(formats[f->index]);
- strlcpy(f->description, name, sizeof(f->description));
f->pixelformat = formats[f->index];
- if (!coda_format_is_yuv(formats[f->index]))
- f->flags |= V4L2_FMT_FLAG_COMPRESSED;
return 0;
}
@@ -504,9 +448,9 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec,
S_ALIGN);
switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
- case V4L2_PIX_FMT_NV12:
/*
* Frame stride must be at least multiple of 8,
* but multiple of 16 for h.264 or JPEG 4:2:x
@@ -645,6 +589,22 @@ static int coda_s_fmt(struct coda_ctx *ctx, struct v4l2_format *f)
q_data->rect.width = f->fmt.pix.width;
q_data->rect.height = f->fmt.pix.height;
+ switch (f->fmt.pix.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ ctx->tiled_map_type = GDI_TILED_FRAME_MB_RASTER_MAP;
+ if (!disable_tiling)
+ break;
+ }
+ /* else fall through */
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP;
+ break;
+ default:
+ break;
+ }
+
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Setting format for type %d, wxh: %dx%d, fmt: %d\n",
f->type, q_data->width, q_data->height, q_data->fourcc);
@@ -831,6 +791,104 @@ static int coda_decoder_cmd(struct file *file, void *fh,
return 0;
}
+static int coda_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+ struct v4l2_fract *tpf;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ a->parm.output.capability = V4L2_CAP_TIMEPERFRAME;
+ tpf = &a->parm.output.timeperframe;
+ tpf->denominator = ctx->params.framerate & CODA_FRATE_RES_MASK;
+ tpf->numerator = 1 + (ctx->params.framerate >>
+ CODA_FRATE_DIV_OFFSET);
+
+ return 0;
+}
+
+/*
+ * Approximate timeperframe v4l2_fract with values that can be written
+ * into the 16-bit CODA_FRATE_DIV and CODA_FRATE_RES fields.
+ */
+static void coda_approximate_timeperframe(struct v4l2_fract *timeperframe)
+{
+ struct v4l2_fract s = *timeperframe;
+ struct v4l2_fract f0;
+ struct v4l2_fract f1 = { 1, 0 };
+ struct v4l2_fract f2 = { 0, 1 };
+ unsigned int i, div, s_denominator;
+
+ /* Lower bound is 1/65535 */
+ if (s.numerator == 0 || s.denominator / s.numerator > 65535) {
+ timeperframe->numerator = 1;
+ timeperframe->denominator = 65535;
+ return;
+ }
+
+ /* Upper bound is 65536/1, map everything above to infinity */
+ if (s.denominator == 0 || s.numerator / s.denominator > 65536) {
+ timeperframe->numerator = 1;
+ timeperframe->denominator = 0;
+ return;
+ }
+
+ /* Reduce fraction to lowest terms */
+ div = gcd(s.numerator, s.denominator);
+ if (div > 1) {
+ s.numerator /= div;
+ s.denominator /= div;
+ }
+
+ if (s.numerator <= 65536 && s.denominator < 65536) {
+ *timeperframe = s;
+ return;
+ }
+
+ /* Find successive convergents from continued fraction expansion */
+ while (f2.numerator <= 65536 && f2.denominator < 65536) {
+ f0 = f1;
+ f1 = f2;
+
+ /* Stop when f2 exactly equals timeperframe */
+ if (s.numerator == 0)
+ break;
+
+ i = s.denominator / s.numerator;
+
+ f2.numerator = f0.numerator + i * f1.numerator;
+ f2.denominator = f0.denominator + i * f2.denominator;
+
+ s_denominator = s.numerator;
+ s.numerator = s.denominator % s.numerator;
+ s.denominator = s_denominator;
+ }
+
+ *timeperframe = f1;
+}
+
+static uint32_t coda_timeperframe_to_frate(struct v4l2_fract *timeperframe)
+{
+ return ((timeperframe->numerator - 1) << CODA_FRATE_DIV_OFFSET) |
+ timeperframe->denominator;
+}
+
+static int coda_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
+{
+ struct coda_ctx *ctx = fh_to_ctx(fh);
+ struct v4l2_fract *tpf;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ tpf = &a->parm.output.timeperframe;
+ coda_approximate_timeperframe(tpf);
+ ctx->params.framerate = coda_timeperframe_to_frate(tpf);
+
+ return 0;
+}
+
static int coda_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
@@ -871,31 +929,13 @@ static const struct v4l2_ioctl_ops coda_ioctl_ops = {
.vidioc_try_decoder_cmd = coda_try_decoder_cmd,
.vidioc_decoder_cmd = coda_decoder_cmd,
+ .vidioc_g_parm = coda_g_parm,
+ .vidioc_s_parm = coda_s_parm,
+
.vidioc_subscribe_event = coda_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
-void coda_set_gdi_regs(struct coda_ctx *ctx)
-{
- struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
- struct coda_dev *dev = ctx->dev;
- int i;
-
- for (i = 0; i < 16; i++)
- coda_write(dev, tiled_map->xy2ca_map[i],
- CODA9_GDI_XY2_CAS_0 + 4 * i);
- for (i = 0; i < 4; i++)
- coda_write(dev, tiled_map->xy2ba_map[i],
- CODA9_GDI_XY2_BA_0 + 4 * i);
- for (i = 0; i < 16; i++)
- coda_write(dev, tiled_map->xy2ra_map[i],
- CODA9_GDI_XY2_RAS_0 + 4 * i);
- coda_write(dev, tiled_map->xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
- for (i = 0; i < 32; i++)
- coda_write(dev, tiled_map->rbc2axi_map[i],
- CODA9_GDI_RBC2_AXI_0 + 4 * i);
-}
-
/*
* Mem-to-mem operations.
*/
@@ -949,14 +989,14 @@ static void coda_pic_run_work(struct work_struct *work)
static int coda_job_ready(void *m2m_priv)
{
struct coda_ctx *ctx = m2m_priv;
+ int src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx);
/*
* For both 'P' and 'key' frame cases 1 picture
* 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->fh.m2m_ctx) &&
- ctx->inst_type != CODA_INST_DECODER) {
+ if (!src_bufs && ctx->inst_type != CODA_INST_DECODER) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"not ready: not enough video buffers.\n");
return 0;
@@ -969,27 +1009,17 @@ static int coda_job_ready(void *m2m_priv)
}
if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) {
- struct list_head *meta;
- bool stream_end;
- int num_metas;
- int src_bufs;
+ bool stream_end = ctx->bit_stream_param &
+ CODA_BIT_STREAM_END_FLAG;
+ int num_metas = ctx->num_metas;
- if (ctx->hold && !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) {
+ if (ctx->hold && !src_bufs) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"%d: not ready: on hold for more buffers.\n",
ctx->idx);
return 0;
}
- stream_end = ctx->bit_stream_param &
- CODA_BIT_STREAM_END_FLAG;
-
- num_metas = 0;
- list_for_each(meta, &ctx->buffer_meta_list)
- num_metas++;
-
- src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx);
-
if (!stream_end && (num_metas + src_bufs) < 2) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"%d: not ready: need 2 buffers available (%d, %d)\n",
@@ -998,8 +1028,8 @@ static int coda_job_ready(void *m2m_priv)
}
- if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) &&
- !stream_end && (coda_get_bitstream_payload(ctx) < 512)) {
+ if (!src_bufs && !stream_end &&
+ (coda_get_bitstream_payload(ctx) < 512)) {
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"%d: not ready: not enough bitstream data (%d).\n",
ctx->idx, coda_get_bitstream_payload(ctx));
@@ -1015,6 +1045,7 @@ static int coda_job_ready(void *m2m_priv)
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"job ready\n");
+
return 1;
}
@@ -1052,32 +1083,6 @@ static const struct v4l2_m2m_ops coda_m2m_ops = {
.unlock = coda_unlock,
};
-static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type)
-{
- struct gdi_tiled_map *tiled_map = &ctx->tiled_map;
- int luma_map, chro_map, i;
-
- memset(tiled_map, 0, sizeof(*tiled_map));
-
- luma_map = 64;
- chro_map = 64;
- tiled_map->map_type = tiled_map_type;
- for (i = 0; i < 16; i++)
- tiled_map->xy2ca_map[i] = luma_map << 8 | chro_map;
- for (i = 0; i < 4; i++)
- tiled_map->xy2ba_map[i] = luma_map << 8 | chro_map;
- for (i = 0; i < 16; i++)
- tiled_map->xy2ra_map[i] = luma_map << 8 | chro_map;
-
- if (tiled_map_type == GDI_LINEAR_FRAME_MAP) {
- tiled_map->xy2rbc_config = 0;
- } else {
- dev_err(&ctx->dev->plat_dev->dev, "invalid map type: %d\n",
- tiled_map_type);
- return;
- }
-}
-
static void set_default_params(struct coda_ctx *ctx)
{
unsigned int max_w, max_h, usize, csize;
@@ -1094,8 +1099,8 @@ static void set_default_params(struct coda_ctx *ctx)
ctx->params.framerate = 30;
/* Default formats for output and input queues */
- ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->codec->src_fourcc;
- ctx->q_data[V4L2_M2M_DST].fourcc = ctx->codec->dst_fourcc;
+ ctx->q_data[V4L2_M2M_SRC].fourcc = ctx->cvd->src_formats[0];
+ ctx->q_data[V4L2_M2M_DST].fourcc = ctx->cvd->dst_formats[0];
ctx->q_data[V4L2_M2M_SRC].width = max_w;
ctx->q_data[V4L2_M2M_SRC].height = max_h;
ctx->q_data[V4L2_M2M_DST].width = max_w;
@@ -1116,8 +1121,11 @@ static void set_default_params(struct coda_ctx *ctx)
ctx->q_data[V4L2_M2M_DST].rect.width = max_w;
ctx->q_data[V4L2_M2M_DST].rect.height = max_h;
- if (ctx->dev->devtype->product == CODA_960)
- coda_set_tiled_map_type(ctx, GDI_LINEAR_FRAME_MAP);
+ /*
+ * Since the RBC2AXI logic only supports a single chroma plane,
+ * macroblock tiling only works for to NV12 pixel format.
+ */
+ ctx->tiled_map_type = GDI_LINEAR_FRAME_MAP;
}
/*
@@ -1244,9 +1252,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
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 ||
- (q_data_src->fourcc == V4L2_PIX_FMT_JPEG &&
- ctx->dev->devtype->product == CODA_7541)) {
+ if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) {
/* copy the buffers that were queued before streamon */
mutex_lock(&ctx->bitstream_mutex);
coda_fill_bitstream(ctx, false);
@@ -1315,7 +1321,6 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count)
goto err;
}
- ctx->initialized = 1;
return ret;
err:
@@ -1334,6 +1339,7 @@ static void coda_stop_streaming(struct vb2_queue *q)
struct coda_ctx *ctx = vb2_get_drv_priv(q);
struct coda_dev *dev = ctx->dev;
struct vb2_buffer *buf;
+ unsigned long flags;
bool stop;
stop = ctx->streamon_out && ctx->streamon_cap;
@@ -1368,20 +1374,23 @@ static void coda_stop_streaming(struct vb2_queue *q)
queue_work(dev->workqueue, &ctx->seq_end_work);
flush_work(&ctx->seq_end_work);
}
- mutex_lock(&ctx->bitstream_mutex);
+ spin_lock_irqsave(&ctx->buffer_meta_lock, flags);
while (!list_empty(&ctx->buffer_meta_list)) {
meta = list_first_entry(&ctx->buffer_meta_list,
struct coda_buffer_meta, list);
list_del(&meta->list);
kfree(meta);
}
- mutex_unlock(&ctx->bitstream_mutex);
+ ctx->num_metas = 0;
+ spin_unlock_irqrestore(&ctx->buffer_meta_lock, flags);
kfifo_init(&ctx->bitstream_fifo,
ctx->bitstream.vaddr, ctx->bitstream.size);
- ctx->initialized = 0;
ctx->runcounter = 0;
ctx->aborting = 0;
}
+
+ if (!ctx->streamon_out && !ctx->streamon_cap)
+ ctx->bit_stream_param &= ~CODA_BIT_STREAM_END_FLAG;
}
static const struct vb2_ops coda_qops = {
@@ -1469,6 +1478,12 @@ static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_JPEG_RESTART_INTERVAL:
ctx->params.jpeg_restart_interval = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_VBV_DELAY:
+ ctx->params.vbv_delay = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_VBV_SIZE:
+ ctx->params.vbv_size = min(ctrl->val * 8192, 0x7fffffff);
+ break;
default:
v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev,
"Invalid control, id=%d, val=%d\n",
@@ -1528,6 +1543,14 @@ static void coda_encode_ctrls(struct coda_ctx *ctx)
v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB, 0,
1920 * 1088 / 256, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VBV_DELAY, 0, 0x7fff, 1, 0);
+ /*
+ * The maximum VBV size value is 0x7fffffff bits,
+ * one bit less than 262144 KiB
+ */
+ v4l2_ctrl_new_std(&ctx->ctrls, &coda_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VBV_SIZE, 0, 262144, 1, 0);
}
static void coda_jpeg_encode_ctrls(struct coda_ctx *ctx)
@@ -1726,6 +1749,7 @@ static int coda_open(struct file *file)
mutex_init(&ctx->bitstream_mutex);
mutex_init(&ctx->buffer_mutex);
INIT_LIST_HEAD(&ctx->buffer_meta_list);
+ spin_lock_init(&ctx->buffer_meta_lock);
coda_lock(ctx);
list_add(&ctx->list, &dev->instances);
@@ -1769,7 +1793,7 @@ static int coda_release(struct file *file)
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
/* In case the instance was not running, we still need to call SEQ_END */
- if (ctx->initialized && ctx->ops->seq_end_work) {
+ if (ctx->ops->seq_end_work) {
queue_work(dev->workqueue, &ctx->seq_end_work);
flush_work(&ctx->seq_end_work);
}
@@ -2157,7 +2181,7 @@ static int coda_probe(struct platform_device *pdev)
/* Get IRAM pool from device tree or platform data */
pool = of_gen_pool_get(np, "iram", 0);
if (!pool && pdata)
- pool = gen_pool_get(pdata->iram_dev);
+ pool = gen_pool_get(pdata->iram_dev, NULL);
if (!pool) {
dev_err(&pdev->dev, "iram pool not available\n");
return -ENOMEM;
diff --git a/drivers/media/platform/coda/coda-gdi.c b/drivers/media/platform/coda/coda-gdi.c
new file mode 100644
index 000000000..aaa7afc68
--- /dev/null
+++ b/drivers/media/platform/coda/coda-gdi.c
@@ -0,0 +1,150 @@
+/*
+ * Coda multi-standard codec IP
+ *
+ * Copyright (C) 2014 Philipp Zabel, Pengutronix
+ *
+ * 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/bitops.h>
+#include "coda.h"
+
+#define XY2_INVERT BIT(7)
+#define XY2_ZERO BIT(6)
+#define XY2_TB_XOR BIT(5)
+#define XY2_XYSEL BIT(4)
+#define XY2_Y (1 << 4)
+#define XY2_X (0 << 4)
+
+#define XY2(luma_sel, luma_bit, chroma_sel, chroma_bit) \
+ (((XY2_##luma_sel) | (luma_bit)) << 8 | \
+ (XY2_##chroma_sel) | (chroma_bit))
+
+static const u16 xy2ca_zero_map[16] = {
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+};
+
+static const u16 xy2ca_tiled_map[16] = {
+ XY2(Y, 0, Y, 0),
+ XY2(Y, 1, Y, 1),
+ XY2(Y, 2, Y, 2),
+ XY2(Y, 3, X, 3),
+ XY2(X, 3, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+ XY2(ZERO, 0, ZERO, 0),
+};
+
+/*
+ * RA[15:0], CA[15:8] are hardwired to contain the 24-bit macroblock
+ * start offset (macroblock size is 16x16 for luma, 16x8 for chroma).
+ * Bits CA[4:0] are set using XY2CA above. BA[3:0] seems to be unused.
+ */
+
+#define RBC_CA (0 << 4)
+#define RBC_BA (1 << 4)
+#define RBC_RA (2 << 4)
+#define RBC_ZERO (3 << 4)
+
+#define RBC(luma_sel, luma_bit, chroma_sel, chroma_bit) \
+ (((RBC_##luma_sel) | (luma_bit)) << 6 | \
+ (RBC_##chroma_sel) | (chroma_bit))
+
+static const u16 rbc2axi_tiled_map[32] = {
+ RBC(ZERO, 0, ZERO, 0),
+ RBC(ZERO, 0, ZERO, 0),
+ RBC(ZERO, 0, ZERO, 0),
+ RBC(CA, 0, CA, 0),
+ RBC(CA, 1, CA, 1),
+ RBC(CA, 2, CA, 2),
+ RBC(CA, 3, CA, 3),
+ RBC(CA, 4, CA, 8),
+ RBC(CA, 8, CA, 9),
+ RBC(CA, 9, CA, 10),
+ RBC(CA, 10, CA, 11),
+ RBC(CA, 11, CA, 12),
+ RBC(CA, 12, CA, 13),
+ RBC(CA, 13, CA, 14),
+ RBC(CA, 14, CA, 15),
+ RBC(CA, 15, RA, 0),
+ RBC(RA, 0, RA, 1),
+ RBC(RA, 1, RA, 2),
+ RBC(RA, 2, RA, 3),
+ RBC(RA, 3, RA, 4),
+ RBC(RA, 4, RA, 5),
+ RBC(RA, 5, RA, 6),
+ RBC(RA, 6, RA, 7),
+ RBC(RA, 7, RA, 8),
+ RBC(RA, 8, RA, 9),
+ RBC(RA, 9, RA, 10),
+ RBC(RA, 10, RA, 11),
+ RBC(RA, 11, RA, 12),
+ RBC(RA, 12, RA, 13),
+ RBC(RA, 13, RA, 14),
+ RBC(RA, 14, RA, 15),
+ RBC(RA, 15, ZERO, 0),
+};
+
+void coda_set_gdi_regs(struct coda_ctx *ctx)
+{
+ struct coda_dev *dev = ctx->dev;
+ const u16 *xy2ca_map;
+ u32 xy2rbc_config;
+ int i;
+
+ switch (ctx->tiled_map_type) {
+ case GDI_LINEAR_FRAME_MAP:
+ default:
+ xy2ca_map = xy2ca_zero_map;
+ xy2rbc_config = 0;
+ break;
+ case GDI_TILED_FRAME_MB_RASTER_MAP:
+ xy2ca_map = xy2ca_tiled_map;
+ xy2rbc_config = CODA9_XY2RBC_TILED_MAP |
+ CODA9_XY2RBC_CA_INC_HOR |
+ (16 - 1) << 12 | (8 - 1) << 4;
+ break;
+ }
+
+ for (i = 0; i < 16; i++)
+ coda_write(dev, xy2ca_map[i],
+ CODA9_GDI_XY2_CAS_0 + 4 * i);
+ for (i = 0; i < 4; i++)
+ coda_write(dev, XY2(ZERO, 0, ZERO, 0),
+ CODA9_GDI_XY2_BA_0 + 4 * i);
+ for (i = 0; i < 16; i++)
+ coda_write(dev, XY2(ZERO, 0, ZERO, 0),
+ CODA9_GDI_XY2_RAS_0 + 4 * i);
+ coda_write(dev, xy2rbc_config, CODA9_GDI_XY2_RBC_CONFIG);
+ if (xy2rbc_config) {
+ for (i = 0; i < 32; i++)
+ coda_write(dev, rbc2axi_tiled_map[i],
+ CODA9_GDI_RBC2_AXI_0 + 4 * i);
+ }
+}
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h
index 8e0af221b..59b2af9c7 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/coda/coda.h
@@ -128,6 +128,8 @@ struct coda_params {
enum v4l2_mpeg_video_multi_slice_mode slice_mode;
u32 framerate;
u16 bitrate;
+ u16 vbv_delay;
+ u32 vbv_size;
u32 slice_max_bits;
u32 slice_max_mb;
};
@@ -165,15 +167,8 @@ struct coda_iram_info {
phys_addr_t next_paddr;
};
-struct gdi_tiled_map {
- int xy2ca_map[16];
- int xy2ba_map[16];
- int xy2ra_map[16];
- int rbc2axi_map[32];
- int xy2rbc_config;
- int map_type;
#define GDI_LINEAR_FRAME_MAP 0
-};
+#define GDI_TILED_FRAME_MB_RASTER_MAP 1
struct coda_ctx;
@@ -227,12 +222,14 @@ struct coda_ctx {
struct coda_buffer_meta frame_metas[CODA_MAX_FRAMEBUFFERS];
u32 frame_errors[CODA_MAX_FRAMEBUFFERS];
struct list_head buffer_meta_list;
+ spinlock_t buffer_meta_lock;
+ int num_metas;
struct coda_aux_buf workbuf;
int num_internal_frames;
int idx;
int reg_idx;
struct coda_iram_info iram_info;
- struct gdi_tiled_map tiled_map;
+ int tiled_map_type;
u32 bit_stream_param;
u32 frm_dis_flg;
u32 frame_mem_ctrl;
diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h
index 7d0262411..3490602fa 100644
--- a/drivers/media/platform/coda/coda_regs.h
+++ b/drivers/media/platform/coda/coda_regs.h
@@ -51,6 +51,7 @@
#define CODA7_STREAM_SEL_64BITS_ENDIAN (1 << 1)
#define CODA_STREAM_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_FRAME_MEM_CTRL 0x110
+#define CODA9_FRAME_TILED2LINEAR (1 << 11)
#define CODA_FRAME_CHROMA_INTERLEAVE (1 << 2)
#define CODA_IMAGE_ENDIAN_SELECT (1 << 0)
#define CODA_REG_BIT_BIT_STREAM_PARAM 0x114
@@ -263,6 +264,10 @@
#define CODADX6_PICHEIGHT_MASK 0x3ff
#define CODA7_PICHEIGHT_MASK 0xffff
#define CODA_CMD_ENC_SEQ_SRC_F_RATE 0x194
+#define CODA_FRATE_RES_OFFSET 0
+#define CODA_FRATE_RES_MASK 0xffff
+#define CODA_FRATE_DIV_OFFSET 16
+#define CODA_FRATE_DIV_MASK 0xffff
#define CODA_CMD_ENC_SEQ_MP4_PARA 0x198
#define CODA_MP4PARAM_VERID_OFFSET 6
#define CODA_MP4PARAM_VERID_MASK 0x01
@@ -448,7 +453,12 @@
#define CODA9_GDI_XY2_RAS_F (CODA9_GDMA_BASE + 0x88c)
#define CODA9_GDI_XY2_RBC_CONFIG (CODA9_GDMA_BASE + 0x890)
+#define CODA9_XY2RBC_SEPARATE_MAP BIT(19)
+#define CODA9_XY2RBC_TOP_BOT_SPLIT BIT(18)
+#define CODA9_XY2RBC_TILED_MAP BIT(17)
+#define CODA9_XY2RBC_CA_INC_HOR BIT(16)
#define CODA9_GDI_RBC2_AXI_0 (CODA9_GDMA_BASE + 0x8a0)
#define CODA9_GDI_RBC2_AXI_1F (CODA9_GDMA_BASE + 0x91c)
+#define CODA9_GDI_TILEDBUF_BASE (CODA9_GDMA_BASE + 0x920)
#endif
diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/coda/trace.h
index 781bf7286..d9099a0f7 100644
--- a/drivers/media/platform/coda/trace.h
+++ b/drivers/media/platform/coda/trace.h
@@ -48,7 +48,7 @@ TRACE_EVENT(coda_bit_done,
TP_printk("minor = %d, ctx = %d", __entry->minor, __entry->ctx)
);
-TRACE_EVENT(coda_enc_pic_run,
+DECLARE_EVENT_CLASS(coda_buf_class,
TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf),
TP_ARGS(ctx, buf),
@@ -69,28 +69,17 @@ TRACE_EVENT(coda_enc_pic_run,
__entry->minor, __entry->index, __entry->ctx)
);
-TRACE_EVENT(coda_enc_pic_done,
+DEFINE_EVENT(coda_buf_class, coda_enc_pic_run,
TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf),
+ TP_ARGS(ctx, buf)
+);
- TP_ARGS(ctx, buf),
-
- TP_STRUCT__entry(
- __field(int, minor)
- __field(int, index)
- __field(int, ctx)
- ),
-
- TP_fast_assign(
- __entry->minor = ctx->fh.vdev->minor;
- __entry->index = buf->v4l2_buf.index;
- __entry->ctx = ctx->idx;
- ),
-
- TP_printk("minor = %d, index = %d, ctx = %d",
- __entry->minor, __entry->index, __entry->ctx)
+DEFINE_EVENT(coda_buf_class, coda_enc_pic_done,
+ TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf),
+ TP_ARGS(ctx, buf)
);
-TRACE_EVENT(coda_bit_queue,
+DECLARE_EVENT_CLASS(coda_buf_meta_class,
TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf,
struct coda_buffer_meta *meta),
@@ -117,7 +106,13 @@ TRACE_EVENT(coda_bit_queue,
__entry->ctx)
);
-TRACE_EVENT(coda_dec_pic_run,
+DEFINE_EVENT(coda_buf_meta_class, coda_bit_queue,
+ TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf,
+ struct coda_buffer_meta *meta),
+ TP_ARGS(ctx, buf, meta)
+);
+
+DECLARE_EVENT_CLASS(coda_meta_class,
TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta),
TP_ARGS(ctx, meta),
@@ -140,54 +135,20 @@ TRACE_EVENT(coda_dec_pic_run,
__entry->minor, __entry->start, __entry->end, __entry->ctx)
);
-TRACE_EVENT(coda_dec_pic_done,
+DEFINE_EVENT(coda_meta_class, coda_dec_pic_run,
TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta),
-
- TP_ARGS(ctx, meta),
-
- TP_STRUCT__entry(
- __field(int, minor)
- __field(int, start)
- __field(int, end)
- __field(int, ctx)
- ),
-
- TP_fast_assign(
- __entry->minor = ctx->fh.vdev->minor;
- __entry->start = meta->start;
- __entry->end = meta->end;
- __entry->ctx = ctx->idx;
- ),
-
- TP_printk("minor = %d, start = 0x%x, end = 0x%x, ctx = %d",
- __entry->minor, __entry->start, __entry->end, __entry->ctx)
+ TP_ARGS(ctx, meta)
);
-TRACE_EVENT(coda_dec_rot_done,
- TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta,
- struct vb2_buffer *buf),
-
- TP_ARGS(ctx, meta, buf),
-
- TP_STRUCT__entry(
- __field(int, minor)
- __field(int, start)
- __field(int, end)
- __field(int, index)
- __field(int, ctx)
- ),
-
- TP_fast_assign(
- __entry->minor = ctx->fh.vdev->minor;
- __entry->start = meta->start;
- __entry->end = meta->end;
- __entry->index = buf->v4l2_buf.index;
- __entry->ctx = ctx->idx;
- ),
+DEFINE_EVENT(coda_meta_class, coda_dec_pic_done,
+ TP_PROTO(struct coda_ctx *ctx, struct coda_buffer_meta *meta),
+ TP_ARGS(ctx, meta)
+);
- TP_printk("minor = %d, start = 0x%x, end = 0x%x, index = %d, ctx = %d",
- __entry->minor, __entry->start, __entry->end, __entry->index,
- __entry->ctx)
+DEFINE_EVENT(coda_buf_meta_class, coda_dec_rot_done,
+ TP_PROTO(struct coda_ctx *ctx, struct vb2_buffer *buf,
+ struct coda_buffer_meta *meta),
+ TP_ARGS(ctx, buf, meta)
);
#endif /* __CODA_TRACE_H__ */
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/exynos4-is/fimc-m2m.c
index 0ad1b6f84..d2bfe7c2a 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/exynos4-is/fimc-m2m.c
@@ -188,7 +188,7 @@ static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
if (IS_ERR(f))
return PTR_ERR(f);
/*
- * Return number of non-contigous planes (plane buffers)
+ * Return number of non-contiguous planes (plane buffers)
* depending on the configured color format.
*/
if (!f->fmt)
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/fsl-viu.c
index 5b76e3db6..ae8c6b35a 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/fsl-viu.c
@@ -28,6 +28,9 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
#include <media/videobuf-dma-contig.h>
#define DRV_NAME "fsl_viu"
@@ -40,49 +43,6 @@
/* I2C address of video decoder chip is 0x4A */
#define VIU_VIDEO_DECODER_ADDR 0x25
-/* supported controls */
-static struct v4l2_queryctrl viu_qctrl[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 255,
- .step = 1,
- .default_value = 127,
- .flags = 0,
- }, {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 0x10,
- .flags = 0,
- }, {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = 0,
- .maximum = 255,
- .step = 0x1,
- .default_value = 127,
- .flags = 0,
- }, {
- .id = V4L2_CID_HUE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Hue",
- .minimum = -128,
- .maximum = 127,
- .step = 0x1,
- .default_value = 0,
- .flags = 0,
- }
-};
-
-static int qctl_regs[ARRAY_SIZE(viu_qctrl)];
-
static int info_level;
#define dprintk(level, fmt, arg...) \
@@ -95,7 +55,6 @@ static int info_level;
* Basic structures
*/
struct viu_fmt {
- char name[32];
u32 fourcc; /* v4l2 format id */
u32 pixelformat;
int depth;
@@ -103,12 +62,10 @@ struct viu_fmt {
static struct viu_fmt formats[] = {
{
- .name = "RGB-16 (5/B-6/G-5/R)",
.fourcc = V4L2_PIX_FMT_RGB565,
.pixelformat = V4L2_PIX_FMT_RGB565,
.depth = 16,
}, {
- .name = "RGB-32 (A-R-G-B)",
.fourcc = V4L2_PIX_FMT_RGB32,
.pixelformat = V4L2_PIX_FMT_RGB32,
.depth = 32,
@@ -156,6 +113,7 @@ struct viu_reg {
struct viu_dev {
struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
struct mutex lock;
spinlock_t slock;
int users;
@@ -195,6 +153,8 @@ struct viu_dev {
};
struct viu_fh {
+ /* must remain the first field of this struct */
+ struct v4l2_fh fh;
struct viu_dev *dev;
/* video capture */
@@ -604,6 +564,7 @@ static int vidioc_querycap(struct file *file, void *priv,
{
strcpy(cap->driver, "viu");
strcpy(cap->card, "viu");
+ strcpy(cap->bus_info, "platform:viu");
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_OVERLAY |
@@ -617,10 +578,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
{
int index = f->index;
- if (f->index > NUM_FORMATS)
+ if (f->index >= NUM_FORMATS)
return -EINVAL;
- strlcpy(f->description, formats[index].name, sizeof(f->description));
f->pixelformat = formats[index].fourcc;
return 0;
}
@@ -637,6 +597,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
f->fmt.pix.bytesperline =
(f->fmt.pix.width * fh->fmt->depth) >> 3;
f->fmt.pix.sizeimage = fh->sizeimage;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
@@ -644,7 +605,6 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct viu_fmt *fmt;
- enum v4l2_field field;
unsigned int maxw, maxh;
fmt = format_by_fourcc(f->fmt.pix.pixelformat);
@@ -654,19 +614,10 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
return -EINVAL;
}
- field = f->fmt.pix.field;
-
- if (field == V4L2_FIELD_ANY) {
- field = V4L2_FIELD_INTERLACED;
- } else if (field != V4L2_FIELD_INTERLACED) {
- dprintk(1, "Field type invalid.\n");
- return -EINVAL;
- }
-
maxw = norm_maxw();
maxh = norm_maxh();
- f->fmt.pix.field = field;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
if (f->fmt.pix.height < 32)
f->fmt.pix.height = 32;
if (f->fmt.pix.height > maxh)
@@ -678,6 +629,8 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
f->fmt.pix.width &= ~0x03;
f->fmt.pix.bytesperline =
(f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
@@ -698,7 +651,6 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
fh->sizeimage = f->fmt.pix.sizeimage;
fh->vb_vidq.field = f->fmt.pix.field;
fh->type = f->type;
- dprintk(1, "set to pixelformat '%4.6s'\n", (char *)&fh->fmt->name);
return 0;
}
@@ -764,8 +716,8 @@ static int viu_setup_preview(struct viu_dev *dev, struct viu_fh *fh)
{
int bpp;
- dprintk(1, "%s %dx%d %s\n", __func__,
- fh->win.w.width, fh->win.w.height, dev->ovfmt->name);
+ dprintk(1, "%s %dx%d\n", __func__,
+ fh->win.w.width, fh->win.w.height);
reg_val.status_cfg = 0;
@@ -1002,58 +954,13 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct viu_fh *fh = priv;
- if (i > 1)
+ if (i)
return -EINVAL;
decoder_call(fh->dev, video, s_routing, i, 0, 0);
return 0;
}
-/* Controls */
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (qc->id && qc->id == viu_qctrl[i].id) {
- memcpy(qc, &(viu_qctrl[i]), sizeof(*qc));
- return 0;
- }
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (ctrl->id == viu_qctrl[i].id) {
- ctrl->value = qctl_regs[i];
- return 0;
- }
- }
- return -EINVAL;
-}
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++) {
- if (ctrl->id == viu_qctrl[i].id) {
- if (ctrl->value < viu_qctrl[i].minimum
- || ctrl->value > viu_qctrl[i].maximum)
- return -ERANGE;
- qctl_regs[i] = ctrl->value;
- return 0;
- }
- }
- return -EINVAL;
-}
-
inline void viu_activate_next_buf(struct viu_dev *dev,
struct viu_dmaqueue *viuq)
{
@@ -1265,7 +1172,6 @@ static int viu_open(struct file *file)
struct viu_reg *vr;
int minor = vdev->minor;
u32 status_cfg;
- int i;
dprintk(1, "viu: open (minor=%d)\n", minor);
@@ -1293,6 +1199,7 @@ static int viu_open(struct file *file)
return -ENOMEM;
}
+ v4l2_fh_init(&fh->fh, vdev);
file->private_data = fh;
fh->dev = dev;
@@ -1303,10 +1210,6 @@ static int viu_open(struct file *file)
dev->crop_current.width = fh->width;
dev->crop_current.height = fh->height;
- /* Put all controls at a sane state */
- for (i = 0; i < ARRAY_SIZE(viu_qctrl); i++)
- qctl_regs[i] = viu_qctrl[i].default_value;
-
dprintk(1, "Open: fh=0x%08lx, dev=0x%08lx, dev->vidq=0x%08lx\n",
(unsigned long)fh, (unsigned long)dev,
(unsigned long)&dev->vidq);
@@ -1332,6 +1235,7 @@ static int viu_open(struct file *file)
fh->type, V4L2_FIELD_INTERLACED,
sizeof(struct viu_buf), fh,
&fh->dev->lock);
+ v4l2_fh_add(&fh->fh);
mutex_unlock(&dev->lock);
return 0;
}
@@ -1364,13 +1268,17 @@ static unsigned int viu_poll(struct file *file, struct poll_table_struct *wait)
struct viu_fh *fh = file->private_data;
struct videobuf_queue *q = &fh->vb_vidq;
struct viu_dev *dev = fh->dev;
- unsigned int res;
+ unsigned long req_events = poll_requested_events(wait);
+ unsigned int res = v4l2_ctrl_poll(file, wait);
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
return POLLERR;
+ if (!(req_events & (POLLIN | POLLRDNORM)))
+ return res;
+
mutex_lock(&dev->lock);
- res = videobuf_poll_stream(file, q, wait);
+ res |= videobuf_poll_stream(file, q, wait);
mutex_unlock(&dev->lock);
return res;
}
@@ -1385,6 +1293,8 @@ static int viu_release(struct file *file)
viu_stop_dma(dev);
videobuf_stop(&fh->vb_vidq);
videobuf_mmap_free(&fh->vb_vidq);
+ v4l2_fh_del(&fh->fh);
+ v4l2_fh_exit(&fh->fh);
mutex_unlock(&dev->lock);
kfree(fh);
@@ -1463,11 +1373,11 @@ static const struct v4l2_ioctl_ops viu_ioctl_ops = {
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static struct video_device viu_template = {
@@ -1543,6 +1453,16 @@ static int viu_of_probe(struct platform_device *op)
}
ad = i2c_get_adapter(0);
+
+ v4l2_ctrl_handler_init(&viu_dev->hdl, 5);
+ if (viu_dev->hdl.error) {
+ ret = viu_dev->hdl.error;
+ dev_err(&op->dev, "couldn't register control\n");
+ goto err_vdev;
+ }
+ /* This control handler will inherit the control(s) from the
+ sub-device(s). */
+ viu_dev->v4l2_dev.ctrl_handler = &viu_dev->hdl;
viu_dev->decoder = v4l2_i2c_new_subdev(&viu_dev->v4l2_dev, ad,
"saa7113", VIU_VIDEO_DECODER_ADDR, NULL);
@@ -1559,7 +1479,7 @@ static int viu_of_probe(struct platform_device *op)
goto err_vdev;
}
- memcpy(vdev, &viu_template, sizeof(viu_template));
+ *vdev = viu_template;
vdev->v4l2_dev = &viu_dev->v4l2_dev;
@@ -1614,6 +1534,7 @@ err_irq:
err_clk:
video_unregister_device(viu_dev->vdev);
err_vdev:
+ v4l2_ctrl_handler_free(&viu_dev->hdl);
mutex_unlock(&viu_dev->lock);
i2c_put_adapter(ad);
v4l2_device_unregister(&viu_dev->v4l2_dev);
@@ -1635,6 +1556,7 @@ static int viu_of_remove(struct platform_device *op)
clk_disable_unprepare(dev->clk);
+ v4l2_ctrl_handler_free(&dev->hdl);
video_unregister_device(dev->vdev);
i2c_put_adapter(client->adapter);
v4l2_device_unregister(&dev->v4l2_dev);
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/omap/Kconfig
index dc2aaab54..217d613b0 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/omap/Kconfig
@@ -10,6 +10,7 @@ config VIDEO_OMAP2_VOUT
select OMAP2_DSS if HAS_IOMEM && ARCH_OMAP2PLUS
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
select VIDEO_OMAP2_VOUT_VRFB if VIDEO_OMAP2_VOUT && OMAP2_VRFB
+ select FRAME_VECTOR
default n
---help---
V4L2 Display driver support for OMAP2/3 based boards.
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/omap/omap_vout.c
index f09c5f17a..70c28d19e 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/omap/omap_vout.c
@@ -195,46 +195,34 @@ static int omap_vout_try_format(struct v4l2_pix_format *pix)
}
/*
- * omap_vout_uservirt_to_phys: This inline function is used to convert user
- * space virtual address to physical address.
+ * omap_vout_get_userptr: Convert user space virtual address to physical
+ * address.
*/
-static unsigned long omap_vout_uservirt_to_phys(unsigned long virtp)
+static int omap_vout_get_userptr(struct videobuf_buffer *vb, u32 virtp,
+ u32 *physp)
{
- unsigned long physp = 0;
- struct vm_area_struct *vma;
- struct mm_struct *mm = current->mm;
+ struct frame_vector *vec;
+ int ret;
/* For kernel direct-mapped memory, take the easy way */
- if (virtp >= PAGE_OFFSET)
- return virt_to_phys((void *) virtp);
-
- down_read(&current->mm->mmap_sem);
- vma = find_vma(mm, virtp);
- if (vma && (vma->vm_flags & VM_IO) && vma->vm_pgoff) {
- /* this will catch, kernel-allocated, mmaped-to-usermode
- addresses */
- physp = (vma->vm_pgoff << PAGE_SHIFT) + (virtp - vma->vm_start);
- up_read(&current->mm->mmap_sem);
- } else {
- /* otherwise, use get_user_pages() for general userland pages */
- int res, nr_pages = 1;
- struct page *pages;
+ if (virtp >= PAGE_OFFSET) {
+ *physp = virt_to_phys((void *)virtp);
+ return 0;
+ }
- res = get_user_pages(current, current->mm, virtp, nr_pages, 1,
- 0, &pages, NULL);
- up_read(&current->mm->mmap_sem);
+ vec = frame_vector_create(1);
+ if (!vec)
+ return -ENOMEM;
- if (res == nr_pages) {
- physp = __pa(page_address(&pages[0]) +
- (virtp & ~PAGE_MASK));
- } else {
- printk(KERN_WARNING VOUT_NAME
- "get_user_pages failed\n");
- return 0;
- }
+ ret = get_vaddr_frames(virtp, 1, true, false, vec);
+ if (ret != 1) {
+ frame_vector_destroy(vec);
+ return -EINVAL;
}
+ *physp = __pfn_to_phys(frame_vector_pfns(vec)[0]);
+ vb->priv = vec;
- return physp;
+ return 0;
}
/*
@@ -784,11 +772,15 @@ static int omap_vout_buffer_prepare(struct videobuf_queue *q,
* address of the buffer
*/
if (V4L2_MEMORY_USERPTR == vb->memory) {
+ int ret;
+
if (0 == vb->baddr)
return -EINVAL;
/* Physical address */
- vout->queued_buf_addr[vb->i] = (u8 *)
- omap_vout_uservirt_to_phys(vb->baddr);
+ ret = omap_vout_get_userptr(vb, vb->baddr,
+ (u32 *)&vout->queued_buf_addr[vb->i]);
+ if (ret < 0)
+ return ret;
} else {
unsigned long addr, dma_addr;
unsigned long size;
@@ -834,12 +826,13 @@ static void omap_vout_buffer_queue(struct videobuf_queue *q,
static void omap_vout_buffer_release(struct videobuf_queue *q,
struct videobuf_buffer *vb)
{
- struct omap_vout_device *vout = q->priv_data;
-
vb->state = VIDEOBUF_NEEDS_INIT;
+ if (vb->memory == V4L2_MEMORY_USERPTR && vb->priv) {
+ struct frame_vector *vec = vb->priv;
- if (V4L2_MEMORY_MMAP != vout->memory)
- return;
+ put_vaddr_frames(vec);
+ frame_vector_destroy(vec);
+ }
}
/*
@@ -872,7 +865,7 @@ static void omap_vout_vm_close(struct vm_area_struct *vma)
vout->mmap_count--;
}
-static struct vm_operations_struct omap_vout_vm_ops = {
+static const struct vm_operations_struct omap_vout_vm_ops = {
.open = omap_vout_vm_open,
.close = omap_vout_vm_close,
};
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c
index 12be830d7..56e683b19 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/omap3isp/isp.c
@@ -101,7 +101,6 @@ static const struct isp_res_mapping isp_res_maps[] = {
0x0000, /* csi2a, len 0x0170 */
0x0170, /* csiphy2, len 0x000c */
},
- .syscon_offset = 0xdc,
.phy_type = ISP_PHY_TYPE_3430,
},
{
@@ -124,7 +123,6 @@ static const struct isp_res_mapping isp_res_maps[] = {
0x0570, /* csiphy1, len 0x000c */
0x05c0, /* csi2c, len 0x0040 (2nd area) */
},
- .syscon_offset = 0x2f0,
.phy_type = ISP_PHY_TYPE_3630,
},
};
@@ -1796,47 +1794,6 @@ static void isp_unregister_entities(struct isp_device *isp)
media_device_unregister(&isp->media_dev);
}
-/*
- * isp_register_subdev - Register a sub-device
- * @isp: OMAP3 ISP device
- * @isp_subdev: platform data related to a sub-device
- *
- * Register an I2C sub-device which has not been registered by other
- * means (such as the Device Tree).
- *
- * Return a pointer to the sub-device if it has been successfully
- * registered, or NULL otherwise.
- */
-static struct v4l2_subdev *
-isp_register_subdev(struct isp_device *isp,
- struct isp_platform_subdev *isp_subdev)
-{
- struct i2c_adapter *adapter;
- struct v4l2_subdev *sd;
-
- if (isp_subdev->board_info == NULL)
- return NULL;
-
- adapter = i2c_get_adapter(isp_subdev->i2c_adapter_id);
- if (adapter == NULL) {
- dev_err(isp->dev,
- "%s: Unable to get I2C adapter %d for device %s\n",
- __func__, isp_subdev->i2c_adapter_id,
- isp_subdev->board_info->type);
- return NULL;
- }
-
- sd = v4l2_i2c_new_subdev_board(&isp->v4l2_dev, adapter,
- isp_subdev->board_info, NULL);
- if (sd == NULL) {
- dev_err(isp->dev, "%s: Unable to register subdev %s\n",
- __func__, isp_subdev->board_info->type);
- return NULL;
- }
-
- return sd;
-}
-
static int isp_link_entity(
struct isp_device *isp, struct media_entity *entity,
enum isp_interface_type interface)
@@ -1910,8 +1867,6 @@ static int isp_link_entity(
static int isp_register_entities(struct isp_device *isp)
{
- struct isp_platform_data *pdata = isp->pdata;
- struct isp_platform_subdev *isp_subdev;
int ret;
isp->media_dev.dev = isp->dev;
@@ -1968,37 +1923,6 @@ static int isp_register_entities(struct isp_device *isp)
if (ret < 0)
goto done;
- /*
- * Device Tree --- the external sub-devices will be registered
- * later. The same goes for the sub-device node registration.
- */
- if (isp->dev->of_node)
- return 0;
-
- /* Register external entities */
- for (isp_subdev = pdata ? pdata->subdevs : NULL;
- isp_subdev && isp_subdev->board_info; isp_subdev++) {
- struct v4l2_subdev *sd;
-
- sd = isp_register_subdev(isp, isp_subdev);
-
- /*
- * No bus information --- this is either a flash or a
- * lens subdev.
- */
- if (!sd || !isp_subdev->bus)
- continue;
-
- sd->host_priv = isp_subdev->bus;
-
- ret = isp_link_entity(isp, &sd->entity,
- isp_subdev->bus->interface);
- if (ret < 0)
- goto done;
- }
-
- ret = v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
-
done:
if (ret < 0)
isp_unregister_entities(isp);
@@ -2402,33 +2326,24 @@ static int isp_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type",
- &isp->phy_type);
- if (ret)
- return ret;
+ ret = of_property_read_u32(pdev->dev.of_node, "ti,phy-type",
+ &isp->phy_type);
+ if (ret)
+ return ret;
- isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "syscon");
- if (IS_ERR(isp->syscon))
- return PTR_ERR(isp->syscon);
+ isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "syscon");
+ if (IS_ERR(isp->syscon))
+ return PTR_ERR(isp->syscon);
- ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1,
- &isp->syscon_offset);
- if (ret)
- return ret;
+ ret = of_property_read_u32_index(pdev->dev.of_node, "syscon", 1,
+ &isp->syscon_offset);
+ if (ret)
+ return ret;
- ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier);
- if (ret < 0)
- return ret;
- } else {
- isp->pdata = pdev->dev.platform_data;
- isp->syscon = syscon_regmap_lookup_by_pdevname("syscon.0");
- if (IS_ERR(isp->syscon))
- return PTR_ERR(isp->syscon);
- dev_warn(&pdev->dev,
- "Platform data support is deprecated! Please move to DT now!\n");
- }
+ ret = isp_of_parse_nodes(&pdev->dev, &isp->notifier);
+ if (ret < 0)
+ return ret;
isp->autoidle = autoidle;
@@ -2507,11 +2422,6 @@ static int isp_probe(struct platform_device *pdev)
goto error_isp;
}
- if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) {
- isp->syscon_offset = isp_res_maps[m].syscon_offset;
- isp->phy_type = isp_res_maps[m].phy_type;
- }
-
for (i = 1; i < OMAP3_ISP_IOMEM_CSI2A_REGS1; i++)
isp->mmio_base[i] =
isp->mmio_base[0] + isp_res_maps[m].offset[i];
@@ -2555,15 +2465,12 @@ static int isp_probe(struct platform_device *pdev)
if (ret < 0)
goto error_modules;
- if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
- isp->notifier.bound = isp_subdev_notifier_bound;
- isp->notifier.complete = isp_subdev_notifier_complete;
+ isp->notifier.bound = isp_subdev_notifier_bound;
+ isp->notifier.complete = isp_subdev_notifier_complete;
- ret = v4l2_async_notifier_register(&isp->v4l2_dev,
- &isp->notifier);
- if (ret)
- goto error_register_entities;
- }
+ ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier);
+ if (ret)
+ goto error_register_entities;
isp_core_init(isp, 1);
omap3isp_put(isp);
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/omap3isp/isp.h
index e57994317..5acc2e651 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/omap3isp/isp.h
@@ -17,7 +17,6 @@
#ifndef OMAP3_ISP_CORE_H
#define OMAP3_ISP_CORE_H
-#include <media/omap3isp.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
#include <linux/clk-provider.h>
@@ -27,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/wait.h>
+#include "omap3isp.h"
#include "ispstat.h"
#include "ispccdc.h"
#include "ispreg.h"
@@ -101,15 +101,11 @@ struct regmap;
* struct isp_res_mapping - Map ISP io resources to ISP revision.
* @isp_rev: ISP_REVISION_x_x
* @offset: register offsets of various ISP sub-blocks
- * @syscon_offset: offset of the syscon register for 343x / 3630
- * (CONTROL_CSIRXFE / CONTROL_CAMERA_PHY_CTRL, respectively)
- * from the syscon base address
* @phy_type: ISP_PHY_TYPE_{3430,3630}
*/
struct isp_res_mapping {
u32 isp_rev;
u32 offset[OMAP3_ISP_IOMEM_LAST];
- u32 syscon_offset;
u32 phy_type;
};
@@ -184,7 +180,6 @@ struct isp_device {
u32 revision;
/* platform HW resources */
- struct isp_platform_data *pdata;
unsigned int irq_num;
void __iomem *mmio_base[OMAP3_ISP_IOMEM_LAST];
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/omap3isp/ispcsiphy.h
index e17c88bea..28b63b28f 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.h
+++ b/drivers/media/platform/omap3isp/ispcsiphy.h
@@ -17,7 +17,7 @@
#ifndef OMAP3_ISP_CSI_PHY_H
#define OMAP3_ISP_CSI_PHY_H
-#include <media/omap3isp.h>
+#include "omap3isp.h"
struct isp_csi2_device;
struct regulator;
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c
index d285af18d..41bb8df91 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/omap3isp/ispvideo.c
@@ -1018,8 +1018,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
pipe->entities = 0;
- if (video->isp->pdata && video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, true);
+ /* TODO: Implement PM QoS */
pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]);
pipe->max_rate = pipe->l3_ick;
@@ -1100,8 +1099,7 @@ err_set_stream:
err_check_format:
media_entity_pipeline_stop(&video->video.entity);
err_pipeline_start:
- if (video->isp->pdata && video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, false);
+ /* TODO: Implement PM QoS */
/* The DMA queue must be emptied here, otherwise CCDC interrupts that
* will get triggered the next time the CCDC is powered up will try to
* access buffers that might have been freed but still present in the
@@ -1161,8 +1159,7 @@ isp_video_streamoff(struct file *file, void *fh, enum v4l2_buf_type type)
video->queue = NULL;
video->error = false;
- if (video->isp->pdata && video->isp->pdata->set_constraints)
- video->isp->pdata->set_constraints(video->isp, false);
+ /* TODO: Implement PM QoS */
media_entity_pipeline_stop(&video->video.entity);
done:
diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/omap3isp/omap3isp.h
new file mode 100644
index 000000000..190e259a6
--- /dev/null
+++ b/drivers/media/platform/omap3isp/omap3isp.h
@@ -0,0 +1,132 @@
+/*
+ * omap3isp.h
+ *
+ * TI OMAP3 ISP - Bus Configuration
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ *
+ * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ * Sakari Ailus <sakari.ailus@iki.fi>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef __OMAP3ISP_H__
+#define __OMAP3ISP_H__
+
+enum isp_interface_type {
+ ISP_INTERFACE_PARALLEL,
+ ISP_INTERFACE_CSI2A_PHY2,
+ ISP_INTERFACE_CCP2B_PHY1,
+ ISP_INTERFACE_CCP2B_PHY2,
+ ISP_INTERFACE_CSI2C_PHY1,
+};
+
+/**
+ * struct isp_parallel_cfg - Parallel interface configuration
+ * @data_lane_shift: Data lane shifter
+ * 0 - CAMEXT[13:0] -> CAM[13:0]
+ * 1 - CAMEXT[13:2] -> CAM[11:0]
+ * 2 - CAMEXT[13:4] -> CAM[9:0]
+ * 3 - CAMEXT[13:6] -> CAM[7:0]
+ * @clk_pol: Pixel clock polarity
+ * 0 - Sample on rising edge, 1 - Sample on falling edge
+ * @hs_pol: Horizontal synchronization polarity
+ * 0 - Active high, 1 - Active low
+ * @vs_pol: Vertical synchronization polarity
+ * 0 - Active high, 1 - Active low
+ * @fld_pol: Field signal polarity
+ * 0 - Positive, 1 - Negative
+ * @data_pol: Data polarity
+ * 0 - Normal, 1 - One's complement
+ */
+struct isp_parallel_cfg {
+ unsigned int data_lane_shift:2;
+ unsigned int clk_pol:1;
+ unsigned int hs_pol:1;
+ unsigned int vs_pol:1;
+ unsigned int fld_pol:1;
+ unsigned int data_pol:1;
+};
+
+enum {
+ ISP_CCP2_PHY_DATA_CLOCK = 0,
+ ISP_CCP2_PHY_DATA_STROBE = 1,
+};
+
+enum {
+ ISP_CCP2_MODE_MIPI = 0,
+ ISP_CCP2_MODE_CCP2 = 1,
+};
+
+/**
+ * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity
+ * @pos: position of the lane
+ * @pol: polarity of the lane
+ */
+struct isp_csiphy_lane {
+ u8 pos;
+ u8 pol;
+};
+
+#define ISP_CSIPHY1_NUM_DATA_LANES 1
+#define ISP_CSIPHY2_NUM_DATA_LANES 2
+
+/**
+ * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration
+ * @data: Configuration of one or two data lanes
+ * @clk: Clock lane configuration
+ */
+struct isp_csiphy_lanes_cfg {
+ struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES];
+ struct isp_csiphy_lane clk;
+};
+
+/**
+ * struct isp_ccp2_cfg - CCP2 interface configuration
+ * @strobe_clk_pol: Strobe/clock polarity
+ * 0 - Non Inverted, 1 - Inverted
+ * @crc: Enable the cyclic redundancy check
+ * @ccp2_mode: Enable CCP2 compatibility mode
+ * ISP_CCP2_MODE_MIPI - MIPI-CSI1 mode
+ * ISP_CCP2_MODE_CCP2 - CCP2 mode
+ * @phy_layer: Physical layer selection
+ * ISP_CCP2_PHY_DATA_CLOCK - Data/clock physical layer
+ * ISP_CCP2_PHY_DATA_STROBE - Data/strobe physical layer
+ * @vpclk_div: Video port output clock control
+ */
+struct isp_ccp2_cfg {
+ unsigned int strobe_clk_pol:1;
+ unsigned int crc:1;
+ unsigned int ccp2_mode:1;
+ unsigned int phy_layer:1;
+ unsigned int vpclk_div:2;
+ struct isp_csiphy_lanes_cfg lanecfg;
+};
+
+/**
+ * struct isp_csi2_cfg - CSI2 interface configuration
+ * @crc: Enable the cyclic redundancy check
+ */
+struct isp_csi2_cfg {
+ unsigned crc:1;
+ struct isp_csiphy_lanes_cfg lanecfg;
+};
+
+struct isp_bus_cfg {
+ enum isp_interface_type interface;
+ union {
+ struct isp_parallel_cfg parallel;
+ struct isp_ccp2_cfg ccp2;
+ struct isp_csi2_cfg csi2;
+ } bus; /* gcc < 4.6.0 chokes on anonymous union initializers */
+};
+
+#endif /* __OMAP3ISP_H__ */
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/rcar_jpu.c
new file mode 100644
index 000000000..2973f070d
--- /dev/null
+++ b/drivers/media/platform/rcar_jpu.c
@@ -0,0 +1,1794 @@
+/*
+ * Author: Mikhail Ulyanov
+ * Copyright (C) 2014-2015 Cogent Embedded, Inc. <source@cogentembedded.com>
+ * Copyright (C) 2014-2015 Renesas Electronics Corporation
+ *
+ * This is based on the drivers/media/platform/s5p-jpeg driver by
+ * Andrzej Pietrasiewicz and Jacek Anaszewski.
+ * Some portions of code inspired by VSP1 driver by Laurent Pinchart.
+ *
+ * TODO in order of priority:
+ * 1) Rotation
+ * 2) Cropping
+ * 3) V4L2_CID_JPEG_ACTIVE_MARKER
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
+
+
+#define DRV_NAME "rcar_jpu"
+
+/*
+ * Align JPEG header end to cache line to make sure we will not have any issues
+ * with cache; additionally to requerment (33.3.27 R01UH0501EJ0100 Rev.1.00)
+ */
+#define JPU_JPEG_HDR_SIZE (ALIGN(0x258, L1_CACHE_BYTES))
+#define JPU_JPEG_MAX_BYTES_PER_PIXEL 2 /* 16 bit precision format */
+#define JPU_JPEG_MIN_SIZE 25 /* SOI + SOF + EOI */
+#define JPU_JPEG_QTBL_SIZE 0x40
+#define JPU_JPEG_HDCTBL_SIZE 0x1c
+#define JPU_JPEG_HACTBL_SIZE 0xb2
+#define JPU_JPEG_HEIGHT_OFFSET 0x91
+#define JPU_JPEG_WIDTH_OFFSET 0x93
+#define JPU_JPEG_SUBS_OFFSET 0x97
+#define JPU_JPEG_QTBL_LUM_OFFSET 0x07
+#define JPU_JPEG_QTBL_CHR_OFFSET 0x4c
+#define JPU_JPEG_HDCTBL_LUM_OFFSET 0xa4
+#define JPU_JPEG_HACTBL_LUM_OFFSET 0xc5
+#define JPU_JPEG_HDCTBL_CHR_OFFSET 0x17c
+#define JPU_JPEG_HACTBL_CHR_OFFSET 0x19d
+#define JPU_JPEG_PADDING_OFFSET 0x24f
+#define JPU_JPEG_LUM 0x00
+#define JPU_JPEG_CHR 0x01
+#define JPU_JPEG_DC 0x00
+#define JPU_JPEG_AC 0x10
+
+#define JPU_JPEG_422 0x21
+#define JPU_JPEG_420 0x22
+
+#define JPU_JPEG_DEFAULT_422_PIX_FMT V4L2_PIX_FMT_NV16M
+#define JPU_JPEG_DEFAULT_420_PIX_FMT V4L2_PIX_FMT_NV12M
+
+/* JPEG markers */
+#define TEM 0x01
+#define SOF0 0xc0
+#define RST 0xd0
+#define SOI 0xd8
+#define EOI 0xd9
+#define DHP 0xde
+#define DHT 0xc4
+#define COM 0xfe
+#define DQT 0xdb
+#define DRI 0xdd
+#define APP0 0xe0
+
+#define JPU_RESET_TIMEOUT 100 /* ms */
+#define JPU_JOB_TIMEOUT 300 /* ms */
+#define JPU_MAX_QUALITY 4
+#define JPU_WIDTH_MIN 16
+#define JPU_HEIGHT_MIN 16
+#define JPU_WIDTH_MAX 4096
+#define JPU_HEIGHT_MAX 4096
+#define JPU_MEMALIGN 8
+
+/* Flags that indicate a format can be used for capture/output */
+#define JPU_FMT_TYPE_OUTPUT 0
+#define JPU_FMT_TYPE_CAPTURE 1
+#define JPU_ENC_CAPTURE (1 << 0)
+#define JPU_ENC_OUTPUT (1 << 1)
+#define JPU_DEC_CAPTURE (1 << 2)
+#define JPU_DEC_OUTPUT (1 << 3)
+
+/*
+ * JPEG registers and bits
+ */
+
+/* JPEG code mode register */
+#define JCMOD 0x00
+#define JCMOD_PCTR (1 << 7)
+#define JCMOD_MSKIP_ENABLE (1 << 5)
+#define JCMOD_DSP_ENC (0 << 3)
+#define JCMOD_DSP_DEC (1 << 3)
+#define JCMOD_REDU (7 << 0)
+#define JCMOD_REDU_422 (1 << 0)
+#define JCMOD_REDU_420 (2 << 0)
+
+/* JPEG code command register */
+#define JCCMD 0x04
+#define JCCMD_SRST (1 << 12)
+#define JCCMD_JEND (1 << 2)
+#define JCCMD_JSRT (1 << 0)
+
+/* JPEG code quantanization table number register */
+#define JCQTN 0x0c
+#define JCQTN_SHIFT(t) (((t) - 1) << 1)
+
+/* JPEG code Huffman table number register */
+#define JCHTN 0x10
+#define JCHTN_AC_SHIFT(t) (((t) << 1) - 1)
+#define JCHTN_DC_SHIFT(t) (((t) - 1) << 1)
+
+#define JCVSZU 0x1c /* JPEG code vertical size upper register */
+#define JCVSZD 0x20 /* JPEG code vertical size lower register */
+#define JCHSZU 0x24 /* JPEG code horizontal size upper register */
+#define JCHSZD 0x28 /* JPEG code horizontal size lower register */
+#define JCSZ_MASK 0xff /* JPEG code h/v size register contains only 1 byte*/
+
+#define JCDTCU 0x2c /* JPEG code data count upper register */
+#define JCDTCM 0x30 /* JPEG code data count middle register */
+#define JCDTCD 0x34 /* JPEG code data count lower register */
+
+/* JPEG interrupt enable register */
+#define JINTE 0x38
+#define JINTE_ERR (7 << 5) /* INT5 + INT6 + INT7 */
+#define JINTE_TRANSF_COMPL (1 << 10)
+
+/* JPEG interrupt status register */
+#define JINTS 0x3c
+#define JINTS_MASK 0x7c68
+#define JINTS_ERR (1 << 5)
+#define JINTS_PROCESS_COMPL (1 << 6)
+#define JINTS_TRANSF_COMPL (1 << 10)
+
+#define JCDERR 0x40 /* JPEG code decode error register */
+#define JCDERR_MASK 0xf /* JPEG code decode error register mask*/
+
+/* JPEG interface encoding */
+#define JIFECNT 0x70
+#define JIFECNT_INFT_422 0
+#define JIFECNT_INFT_420 1
+#define JIFECNT_SWAP_WB (3 << 4) /* to JPU */
+
+#define JIFESYA1 0x74 /* encode source Y address register 1 */
+#define JIFESCA1 0x78 /* encode source C address register 1 */
+#define JIFESYA2 0x7c /* encode source Y address register 2 */
+#define JIFESCA2 0x80 /* encode source C address register 2 */
+#define JIFESMW 0x84 /* encode source memory width register */
+#define JIFESVSZ 0x88 /* encode source vertical size register */
+#define JIFESHSZ 0x8c /* encode source horizontal size register */
+#define JIFEDA1 0x90 /* encode destination address register 1 */
+#define JIFEDA2 0x94 /* encode destination address register 2 */
+
+/* JPEG decoding control register */
+#define JIFDCNT 0xa0
+#define JIFDCNT_SWAP_WB (3 << 1) /* from JPU */
+
+#define JIFDSA1 0xa4 /* decode source address register 1 */
+#define JIFDDMW 0xb0 /* decode destination memory width register */
+#define JIFDDVSZ 0xb4 /* decode destination vert. size register */
+#define JIFDDHSZ 0xb8 /* decode destination horiz. size register */
+#define JIFDDYA1 0xbc /* decode destination Y address register 1 */
+#define JIFDDCA1 0xc0 /* decode destination C address register 1 */
+
+#define JCQTBL(n) (0x10000 + (n) * 0x40) /* quantization tables regs */
+#define JCHTBD(n) (0x10100 + (n) * 0x100) /* Huffman table DC regs */
+#define JCHTBA(n) (0x10120 + (n) * 0x100) /* Huffman table AC regs */
+
+/**
+ * struct jpu - JPEG IP abstraction
+ * @mutex: the mutex protecting this structure
+ * @lock: spinlock protecting the device contexts
+ * @v4l2_dev: v4l2 device for mem2mem mode
+ * @vfd_encoder: video device node for encoder mem2mem mode
+ * @vfd_decoder: video device node for decoder mem2mem mode
+ * @m2m_dev: v4l2 mem2mem device data
+ * @curr: pointer to current context
+ * @irq_queue: interrupt handler waitqueue
+ * @regs: JPEG IP registers mapping
+ * @irq: JPEG IP irq
+ * @clk: JPEG IP clock
+ * @dev: JPEG IP struct device
+ * @alloc_ctx: videobuf2 memory allocator's context
+ * @ref_count: reference counter
+ */
+struct jpu {
+ struct mutex mutex;
+ spinlock_t lock;
+ struct v4l2_device v4l2_dev;
+ struct video_device vfd_encoder;
+ struct video_device vfd_decoder;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct jpu_ctx *curr;
+ wait_queue_head_t irq_queue;
+
+ void __iomem *regs;
+ unsigned int irq;
+ struct clk *clk;
+ struct device *dev;
+ void *alloc_ctx;
+ int ref_count;
+};
+
+/**
+ * struct jpu_buffer - driver's specific video buffer
+ * @buf: m2m buffer
+ * @compr_quality: destination image quality in compression mode
+ * @subsampling: source image subsampling in decompression mode
+ */
+struct jpu_buffer {
+ struct v4l2_m2m_buffer buf;
+ unsigned short compr_quality;
+ unsigned char subsampling;
+};
+
+/**
+ * struct jpu_fmt - driver's internal format data
+ * @fourcc: the fourcc code, 0 if not applicable
+ * @colorspace: the colorspace specifier
+ * @bpp: number of bits per pixel per plane
+ * @h_align: horizontal alignment order (align to 2^h_align)
+ * @v_align: vertical alignment order (align to 2^v_align)
+ * @subsampling: (horizontal:4 | vertical:4) subsampling factor
+ * @num_planes: number of planes
+ * @types: types of queue this format is applicable to
+ */
+struct jpu_fmt {
+ u32 fourcc;
+ u32 colorspace;
+ u8 bpp[2];
+ u8 h_align;
+ u8 v_align;
+ u8 subsampling;
+ u8 num_planes;
+ u16 types;
+};
+
+/**
+ * jpu_q_data - parameters of one queue
+ * @fmtinfo: driver-specific format of this queue
+ * @format: multiplanar format of this queue
+ * @sequence: sequence number
+ */
+struct jpu_q_data {
+ struct jpu_fmt *fmtinfo;
+ struct v4l2_pix_format_mplane format;
+ unsigned int sequence;
+};
+
+/**
+ * jpu_ctx - the device context data
+ * @jpu: JPEG IP device for this context
+ * @encoder: compression (encode) operation or decompression (decode)
+ * @compr_quality: destination image quality in compression (encode) mode
+ * @out_q: source (output) queue information
+ * @cap_q: destination (capture) queue information
+ * @fh: file handler
+ * @ctrl_handler: controls handler
+ */
+struct jpu_ctx {
+ struct jpu *jpu;
+ bool encoder;
+ unsigned short compr_quality;
+ struct jpu_q_data out_q;
+ struct jpu_q_data cap_q;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+};
+
+ /**
+ * jpeg_buffer - description of memory containing input JPEG data
+ * @end: end position in the buffer
+ * @curr: current position in the buffer
+ */
+struct jpeg_buffer {
+ void *end;
+ void *curr;
+};
+
+static struct jpu_fmt jpu_formats[] = {
+ { V4L2_PIX_FMT_JPEG, V4L2_COLORSPACE_JPEG,
+ {0, 0}, 0, 0, 0, 1, JPU_ENC_CAPTURE | JPU_DEC_OUTPUT },
+ { V4L2_PIX_FMT_NV16M, V4L2_COLORSPACE_SRGB,
+ {8, 8}, 2, 2, JPU_JPEG_422, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
+ { V4L2_PIX_FMT_NV12M, V4L2_COLORSPACE_SRGB,
+ {8, 4}, 2, 2, JPU_JPEG_420, 2, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
+ { V4L2_PIX_FMT_NV16, V4L2_COLORSPACE_SRGB,
+ {16, 0}, 2, 2, JPU_JPEG_422, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
+ { V4L2_PIX_FMT_NV12, V4L2_COLORSPACE_SRGB,
+ {12, 0}, 2, 2, JPU_JPEG_420, 1, JPU_ENC_OUTPUT | JPU_DEC_CAPTURE },
+};
+
+static const u8 zigzag[] = {
+ 0x03, 0x02, 0x0b, 0x13, 0x0a, 0x01, 0x00, 0x09,
+ 0x12, 0x1b, 0x23, 0x1a, 0x11, 0x08, 0x07, 0x06,
+ 0x0f, 0x10, 0x19, 0x22, 0x2b, 0x33, 0x2a, 0x21,
+ 0x18, 0x17, 0x0e, 0x05, 0x04, 0x0d, 0x16, 0x1f,
+ 0x20, 0x29, 0x32, 0x3b, 0x3a, 0x31, 0x28, 0x27,
+ 0x1e, 0x15, 0x0e, 0x14, 0x10, 0x26, 0x2f, 0x30,
+ 0x39, 0x38, 0x37, 0x2e, 0x25, 0x1c, 0x24, 0x2b,
+ 0x36, 0x3f, 0x3e, 0x35, 0x2c, 0x34, 0x3d, 0x3c
+};
+
+#define QTBL_SIZE (ALIGN(JPU_JPEG_QTBL_SIZE, \
+ sizeof(unsigned int)) / sizeof(unsigned int))
+#define HDCTBL_SIZE (ALIGN(JPU_JPEG_HDCTBL_SIZE, \
+ sizeof(unsigned int)) / sizeof(unsigned int))
+#define HACTBL_SIZE (ALIGN(JPU_JPEG_HACTBL_SIZE, \
+ sizeof(unsigned int)) / sizeof(unsigned int))
+/*
+ * Start of image; Quantization tables
+ * SOF0 (17 bytes payload) is Baseline DCT - Sample precision, height, width,
+ * Number of image components, (Ci:8 - Hi:4 - Vi:4 - Tq:8) * 3 - Y,Cb,Cr;
+ * Huffman tables; Padding with 0xff (33.3.27 R01UH0501EJ0100 Rev.1.00)
+ */
+#define JPU_JPEG_HDR_BLOB { \
+ 0xff, SOI, 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_LUM, \
+ [JPU_JPEG_QTBL_LUM_OFFSET ... \
+ JPU_JPEG_QTBL_LUM_OFFSET + JPU_JPEG_QTBL_SIZE - 1] = 0x00, \
+ 0xff, DQT, 0x00, JPU_JPEG_QTBL_SIZE + 0x3, JPU_JPEG_CHR, \
+ [JPU_JPEG_QTBL_CHR_OFFSET ... JPU_JPEG_QTBL_CHR_OFFSET + \
+ JPU_JPEG_QTBL_SIZE - 1] = 0x00, 0xff, SOF0, 0x00, 0x11, 0x08, \
+ [JPU_JPEG_HEIGHT_OFFSET ... JPU_JPEG_HEIGHT_OFFSET + 1] = 0x00, \
+ [JPU_JPEG_WIDTH_OFFSET ... JPU_JPEG_WIDTH_OFFSET + 1] = 0x00, \
+ 0x03, 0x01, [JPU_JPEG_SUBS_OFFSET] = 0x00, JPU_JPEG_LUM, \
+ 0x02, 0x11, JPU_JPEG_CHR, 0x03, 0x11, JPU_JPEG_CHR, \
+ 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_DC, \
+ [JPU_JPEG_HDCTBL_LUM_OFFSET ... \
+ JPU_JPEG_HDCTBL_LUM_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \
+ 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_LUM|JPU_JPEG_AC, \
+ [JPU_JPEG_HACTBL_LUM_OFFSET ... \
+ JPU_JPEG_HACTBL_LUM_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \
+ 0xff, DHT, 0x00, JPU_JPEG_HDCTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_DC, \
+ [JPU_JPEG_HDCTBL_CHR_OFFSET ... \
+ JPU_JPEG_HDCTBL_CHR_OFFSET + JPU_JPEG_HDCTBL_SIZE - 1] = 0x00, \
+ 0xff, DHT, 0x00, JPU_JPEG_HACTBL_SIZE + 0x3, JPU_JPEG_CHR|JPU_JPEG_AC, \
+ [JPU_JPEG_HACTBL_CHR_OFFSET ... \
+ JPU_JPEG_HACTBL_CHR_OFFSET + JPU_JPEG_HACTBL_SIZE - 1] = 0x00, \
+ [JPU_JPEG_PADDING_OFFSET ... JPU_JPEG_HDR_SIZE - 1] = 0xff \
+}
+
+static unsigned char jpeg_hdrs[JPU_MAX_QUALITY][JPU_JPEG_HDR_SIZE] = {
+ [0 ... JPU_MAX_QUALITY - 1] = JPU_JPEG_HDR_BLOB
+};
+
+static const unsigned int qtbl_lum[JPU_MAX_QUALITY][QTBL_SIZE] = {
+ {
+ 0x14101927, 0x322e3e44, 0x10121726, 0x26354144,
+ 0x19171f26, 0x35414444, 0x27262635, 0x41444444,
+ 0x32263541, 0x44444444, 0x2e354144, 0x44444444,
+ 0x3e414444, 0x44444444, 0x44444444, 0x44444444
+ },
+ {
+ 0x100b0b10, 0x171b1f1e, 0x0b0c0c0f, 0x1417171e,
+ 0x0b0c0d10, 0x171a232f, 0x100f1017, 0x1a252f40,
+ 0x1714171a, 0x27334040, 0x1b171a25, 0x33404040,
+ 0x1f17232f, 0x40404040, 0x1e1e2f40, 0x40404040
+ },
+ {
+ 0x0c08080c, 0x11151817, 0x0809090b, 0x0f131217,
+ 0x08090a0c, 0x13141b24, 0x0c0b0c15, 0x141c2435,
+ 0x110f1314, 0x1e27333b, 0x1513141c, 0x27333b3b,
+ 0x18121b24, 0x333b3b3b, 0x17172435, 0x3b3b3b3b
+ },
+ {
+ 0x08060608, 0x0c0e1011, 0x06060608, 0x0a0d0c0f,
+ 0x06060708, 0x0d0e1218, 0x0808080e, 0x0d131823,
+ 0x0c0a0d0d, 0x141a2227, 0x0e0d0e13, 0x1a222727,
+ 0x100c1318, 0x22272727, 0x110f1823, 0x27272727
+ }
+};
+
+static const unsigned int qtbl_chr[JPU_MAX_QUALITY][QTBL_SIZE] = {
+ {
+ 0x15192026, 0x36444444, 0x191c1826, 0x36444444,
+ 0x2018202b, 0x42444444, 0x26262b35, 0x44444444,
+ 0x36424444, 0x44444444, 0x44444444, 0x44444444,
+ 0x44444444, 0x44444444, 0x44444444, 0x44444444
+ },
+ {
+ 0x110f1115, 0x141a2630, 0x0f131211, 0x141a232b,
+ 0x11121416, 0x1a1e2e35, 0x1511161c, 0x1e273540,
+ 0x14141a1e, 0x27304040, 0x1a1a1e27, 0x303f4040,
+ 0x26232e35, 0x40404040, 0x302b3540, 0x40404040
+ },
+ {
+ 0x0d0b0d10, 0x14141d25, 0x0b0e0e0e, 0x10141a20,
+ 0x0d0e0f11, 0x14172328, 0x100e1115, 0x171e2832,
+ 0x14101417, 0x1e25323b, 0x1414171e, 0x25303b3b,
+ 0x1d1a2328, 0x323b3b3b, 0x25202832, 0x3b3b3b3b
+ },
+ {
+ 0x0908090b, 0x0e111318, 0x080a090b, 0x0e0d1116,
+ 0x09090d0e, 0x0d0f171a, 0x0b0b0e0e, 0x0f141a21,
+ 0x0e0e0d0f, 0x14182127, 0x110d0f14, 0x18202727,
+ 0x1311171a, 0x21272727, 0x18161a21, 0x27272727
+ }
+};
+
+static const unsigned int hdctbl_lum[HDCTBL_SIZE] = {
+ 0x00010501, 0x01010101, 0x01000000, 0x00000000,
+ 0x00010203, 0x04050607, 0x08090a0b
+};
+
+static const unsigned int hdctbl_chr[HDCTBL_SIZE] = {
+ 0x00010501, 0x01010101, 0x01000000, 0x00000000,
+ 0x00010203, 0x04050607, 0x08090a0b
+};
+
+static const unsigned int hactbl_lum[HACTBL_SIZE] = {
+ 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512,
+ 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0,
+ 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839,
+ 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869,
+ 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798,
+ 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5,
+ 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea,
+ 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000
+};
+
+static const unsigned int hactbl_chr[HACTBL_SIZE] = {
+ 0x00020103, 0x03020403, 0x05050404, 0x0000017d, 0x01020300, 0x04110512,
+ 0x21314106, 0x13516107, 0x22711432, 0x8191a108, 0x2342b1c1, 0x1552d1f0,
+ 0x24336272, 0x82090a16, 0x1718191a, 0x25262728, 0x292a3435, 0x36373839,
+ 0x3a434445, 0x46474849, 0x4a535455, 0x56575859, 0x5a636465, 0x66676869,
+ 0x6a737475, 0x76777879, 0x7a838485, 0x86878889, 0x8a929394, 0x95969798,
+ 0x999aa2a3, 0xa4a5a6a7, 0xa8a9aab2, 0xb3b4b5b6, 0xb7b8b9ba, 0xc2c3c4c5,
+ 0xc6c7c8c9, 0xcad2d3d4, 0xd5d6d7d8, 0xd9dae1e2, 0xe3e4e5e6, 0xe7e8e9ea,
+ 0xf1f2f3f4, 0xf5f6f7f8, 0xf9fa0000
+};
+
+static const char *error_to_text[16] = {
+ "Normal",
+ "SOI not detected",
+ "SOF1 to SOFF detected",
+ "Subsampling not detected",
+ "SOF accuracy error",
+ "DQT accuracy error",
+ "Component error 1",
+ "Component error 2",
+ "SOF0, DQT, and DHT not detected when SOS detected",
+ "SOS not detected",
+ "EOI not detected",
+ "Restart interval data number error detected",
+ "Image size error",
+ "Last MCU data number error",
+ "Block data number error",
+ "Unknown"
+};
+
+static struct jpu_buffer *vb2_to_jpu_buffer(struct vb2_buffer *vb)
+{
+ struct v4l2_m2m_buffer *b =
+ container_of(vb, struct v4l2_m2m_buffer, vb);
+
+ return container_of(b, struct jpu_buffer, buf);
+}
+
+static u32 jpu_read(struct jpu *jpu, unsigned int reg)
+{
+ return ioread32(jpu->regs + reg);
+}
+
+static void jpu_write(struct jpu *jpu, u32 val, unsigned int reg)
+{
+ iowrite32(val, jpu->regs + reg);
+}
+
+static struct jpu_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
+{
+ return container_of(c->handler, struct jpu_ctx, ctrl_handler);
+}
+
+static struct jpu_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct jpu_ctx, fh);
+}
+
+static void jpu_set_tbl(struct jpu *jpu, u32 reg, const unsigned int *tbl,
+ unsigned int len) {
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ jpu_write(jpu, tbl[i], reg + (i << 2));
+}
+
+static void jpu_set_qtbl(struct jpu *jpu, unsigned short quality)
+{
+ jpu_set_tbl(jpu, JCQTBL(0), qtbl_lum[quality], QTBL_SIZE);
+ jpu_set_tbl(jpu, JCQTBL(1), qtbl_chr[quality], QTBL_SIZE);
+}
+
+static void jpu_set_htbl(struct jpu *jpu)
+{
+ jpu_set_tbl(jpu, JCHTBD(0), hdctbl_lum, HDCTBL_SIZE);
+ jpu_set_tbl(jpu, JCHTBA(0), hactbl_lum, HACTBL_SIZE);
+ jpu_set_tbl(jpu, JCHTBD(1), hdctbl_chr, HDCTBL_SIZE);
+ jpu_set_tbl(jpu, JCHTBA(1), hactbl_chr, HACTBL_SIZE);
+}
+
+static int jpu_wait_reset(struct jpu *jpu)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(JPU_RESET_TIMEOUT);
+
+ while (jpu_read(jpu, JCCMD) & JCCMD_SRST) {
+ if (time_after(jiffies, timeout)) {
+ dev_err(jpu->dev, "timed out in reset\n");
+ return -ETIMEDOUT;
+ }
+ schedule();
+ }
+
+ return 0;
+}
+
+static int jpu_reset(struct jpu *jpu)
+{
+ jpu_write(jpu, JCCMD_SRST, JCCMD);
+ return jpu_wait_reset(jpu);
+}
+
+/*
+ * ============================================================================
+ * video ioctl operations
+ * ============================================================================
+ */
+static void put_qtbl(u8 *p, const u8 *qtbl)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(zigzag); i++)
+ p[i] = *(qtbl + zigzag[i]);
+}
+
+static void put_htbl(u8 *p, const u8 *htbl, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < len; i += 4)
+ for (j = 0; j < 4 && (i + j) < len; ++j)
+ p[i + j] = htbl[i + 3 - j];
+}
+
+static void jpu_generate_hdr(unsigned short quality, unsigned char *p)
+{
+ put_qtbl(p + JPU_JPEG_QTBL_LUM_OFFSET, (const u8 *)qtbl_lum[quality]);
+ put_qtbl(p + JPU_JPEG_QTBL_CHR_OFFSET, (const u8 *)qtbl_chr[quality]);
+
+ put_htbl(p + JPU_JPEG_HDCTBL_LUM_OFFSET, (const u8 *)hdctbl_lum,
+ JPU_JPEG_HDCTBL_SIZE);
+ put_htbl(p + JPU_JPEG_HACTBL_LUM_OFFSET, (const u8 *)hactbl_lum,
+ JPU_JPEG_HACTBL_SIZE);
+
+ put_htbl(p + JPU_JPEG_HDCTBL_CHR_OFFSET, (const u8 *)hdctbl_chr,
+ JPU_JPEG_HDCTBL_SIZE);
+ put_htbl(p + JPU_JPEG_HACTBL_CHR_OFFSET, (const u8 *)hactbl_chr,
+ JPU_JPEG_HACTBL_SIZE);
+}
+
+static int get_byte(struct jpeg_buffer *buf)
+{
+ if (buf->curr >= buf->end)
+ return -1;
+
+ return *(u8 *)buf->curr++;
+}
+
+static int get_word_be(struct jpeg_buffer *buf, unsigned int *word)
+{
+ if (buf->end - buf->curr < 2)
+ return -1;
+
+ *word = get_unaligned_be16(buf->curr);
+ buf->curr += 2;
+
+ return 0;
+}
+
+static void skip(struct jpeg_buffer *buf, unsigned long len)
+{
+ buf->curr += min((unsigned long)(buf->end - buf->curr), len);
+}
+
+static u8 jpu_parse_hdr(void *buffer, unsigned long size, unsigned int *width,
+ unsigned int *height)
+{
+ struct jpeg_buffer jpeg_buffer;
+ unsigned int word;
+ bool soi = false;
+
+ jpeg_buffer.end = buffer + size;
+ jpeg_buffer.curr = buffer;
+
+ /*
+ * basic size check and EOI - we don't want to let JPU cross
+ * buffer bounds in any case. Hope it's stopping by EOI.
+ */
+ if (size < JPU_JPEG_MIN_SIZE || *(u8 *)(buffer + size - 1) != EOI)
+ return 0;
+
+ for (;;) {
+ int c;
+
+ /* skip preceding filler bytes */
+ do
+ c = get_byte(&jpeg_buffer);
+ while (c == 0xff || c == 0);
+
+ if (!soi && c == SOI) {
+ soi = true;
+ continue;
+ } else if (soi != (c != SOI))
+ return 0;
+
+ switch (c) {
+ case SOF0: /* SOF0: baseline JPEG */
+ skip(&jpeg_buffer, 3); /* segment length and bpp */
+ if (get_word_be(&jpeg_buffer, height) ||
+ get_word_be(&jpeg_buffer, width) ||
+ get_byte(&jpeg_buffer) != 3) /* YCbCr only */
+ return 0;
+
+ skip(&jpeg_buffer, 1);
+ return get_byte(&jpeg_buffer);
+ case DHT:
+ case DQT:
+ case COM:
+ case DRI:
+ case APP0 ... APP0 + 0x0f:
+ if (get_word_be(&jpeg_buffer, &word))
+ return 0;
+ skip(&jpeg_buffer, (long)word - 2);
+ case 0:
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int jpu_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+
+ if (ctx->encoder)
+ strlcpy(cap->card, DRV_NAME " encoder", sizeof(cap->card));
+ else
+ strlcpy(cap->card, DRV_NAME " decoder", sizeof(cap->card));
+
+ strlcpy(cap->driver, DRV_NAME, sizeof(cap->driver));
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+ dev_name(ctx->jpu->dev));
+ cap->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M_MPLANE;
+ cap->capabilities = V4L2_CAP_DEVICE_CAPS | cap->device_caps;
+ memset(cap->reserved, 0, sizeof(cap->reserved));
+
+ return 0;
+}
+
+static struct jpu_fmt *jpu_find_format(bool encoder, u32 pixelformat,
+ unsigned int fmt_type)
+{
+ unsigned int i, fmt_flag;
+
+ if (encoder)
+ fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_ENC_OUTPUT :
+ JPU_ENC_CAPTURE;
+ else
+ fmt_flag = fmt_type == JPU_FMT_TYPE_OUTPUT ? JPU_DEC_OUTPUT :
+ JPU_DEC_CAPTURE;
+
+ for (i = 0; i < ARRAY_SIZE(jpu_formats); i++) {
+ struct jpu_fmt *fmt = &jpu_formats[i];
+
+ if (fmt->fourcc == pixelformat && fmt->types & fmt_flag)
+ return fmt;
+ }
+
+ return NULL;
+}
+
+static int jpu_enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ unsigned int i, num = 0;
+
+ for (i = 0; i < ARRAY_SIZE(jpu_formats); ++i) {
+ if (jpu_formats[i].types & type) {
+ if (num == f->index)
+ break;
+ ++num;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(jpu_formats))
+ return -EINVAL;
+
+ f->pixelformat = jpu_formats[i].fourcc;
+
+ return 0;
+}
+
+static int jpu_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+
+ return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_CAPTURE :
+ JPU_DEC_CAPTURE);
+}
+
+static int jpu_enum_fmt_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+
+ return jpu_enum_fmt(f, ctx->encoder ? JPU_ENC_OUTPUT : JPU_DEC_OUTPUT);
+}
+
+static struct jpu_q_data *jpu_get_q_data(struct jpu_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &ctx->out_q;
+ else
+ return &ctx->cap_q;
+}
+
+static void jpu_bound_align_image(u32 *w, unsigned int w_min,
+ unsigned int w_max, unsigned int w_align,
+ u32 *h, unsigned int h_min,
+ unsigned int h_max, unsigned int h_align)
+{
+ unsigned int width, height, w_step, h_step;
+
+ width = *w;
+ height = *h;
+
+ w_step = 1U << w_align;
+ h_step = 1U << h_align;
+ v4l_bound_align_image(w, w_min, w_max, w_align, h, h_min, h_max,
+ h_align, 3);
+
+ if (*w < width && *w + w_step < w_max)
+ *w += w_step;
+ if (*h < height && *h + h_step < h_max)
+ *h += h_step;
+}
+
+static int __jpu_try_fmt(struct jpu_ctx *ctx, struct jpu_fmt **fmtinfo,
+ struct v4l2_pix_format_mplane *pix,
+ enum v4l2_buf_type type)
+{
+ struct jpu_fmt *fmt;
+ unsigned int f_type, w, h;
+
+ f_type = V4L2_TYPE_IS_OUTPUT(type) ? JPU_FMT_TYPE_OUTPUT :
+ JPU_FMT_TYPE_CAPTURE;
+
+ fmt = jpu_find_format(ctx->encoder, pix->pixelformat, f_type);
+ if (!fmt) {
+ unsigned int pixelformat;
+
+ dev_dbg(ctx->jpu->dev, "unknown format; set default format\n");
+ if (ctx->encoder)
+ pixelformat = f_type == JPU_FMT_TYPE_OUTPUT ?
+ V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG;
+ else
+ pixelformat = f_type == JPU_FMT_TYPE_CAPTURE ?
+ V4L2_PIX_FMT_NV16M : V4L2_PIX_FMT_JPEG;
+ fmt = jpu_find_format(ctx->encoder, pixelformat, f_type);
+ }
+
+ pix->pixelformat = fmt->fourcc;
+ pix->colorspace = fmt->colorspace;
+ pix->field = V4L2_FIELD_NONE;
+ pix->num_planes = fmt->num_planes;
+ memset(pix->reserved, 0, sizeof(pix->reserved));
+
+ jpu_bound_align_image(&pix->width, JPU_WIDTH_MIN, JPU_WIDTH_MAX,
+ fmt->h_align, &pix->height, JPU_HEIGHT_MIN,
+ JPU_HEIGHT_MAX, fmt->v_align);
+
+ w = pix->width;
+ h = pix->height;
+
+ if (fmt->fourcc == V4L2_PIX_FMT_JPEG) {
+ /* ignore userspaces's sizeimage for encoding */
+ if (pix->plane_fmt[0].sizeimage <= 0 || ctx->encoder)
+ pix->plane_fmt[0].sizeimage = JPU_JPEG_HDR_SIZE +
+ (JPU_JPEG_MAX_BYTES_PER_PIXEL * w * h);
+ pix->plane_fmt[0].bytesperline = 0;
+ memset(pix->plane_fmt[0].reserved, 0,
+ sizeof(pix->plane_fmt[0].reserved));
+ } else {
+ unsigned int i, bpl = 0;
+
+ for (i = 0; i < pix->num_planes; ++i)
+ bpl = max(bpl, pix->plane_fmt[i].bytesperline);
+
+ bpl = clamp_t(unsigned int, bpl, w, JPU_WIDTH_MAX);
+ bpl = round_up(bpl, JPU_MEMALIGN);
+
+ for (i = 0; i < pix->num_planes; ++i) {
+ pix->plane_fmt[i].bytesperline = bpl;
+ pix->plane_fmt[i].sizeimage = bpl * h * fmt->bpp[i] / 8;
+ memset(pix->plane_fmt[i].reserved, 0,
+ sizeof(pix->plane_fmt[i].reserved));
+ }
+ }
+
+ if (fmtinfo)
+ *fmtinfo = fmt;
+
+ return 0;
+}
+
+static int jpu_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+
+ if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
+ return -EINVAL;
+
+ return __jpu_try_fmt(ctx, NULL, &f->fmt.pix_mp, f->type);
+}
+
+static int jpu_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ struct jpu_fmt *fmtinfo;
+ struct jpu_q_data *q_data;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->jpu->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = __jpu_try_fmt(ctx, &fmtinfo, &f->fmt.pix_mp, f->type);
+ if (ret < 0)
+ return ret;
+
+ q_data = jpu_get_q_data(ctx, f->type);
+
+ q_data->format = f->fmt.pix_mp;
+ q_data->fmtinfo = fmtinfo;
+
+ return 0;
+}
+
+static int jpu_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct jpu_q_data *q_data;
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+
+ if (!v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type))
+ return -EINVAL;
+
+ q_data = jpu_get_q_data(ctx, f->type);
+ f->fmt.pix_mp = q_data->format;
+
+ return 0;
+}
+
+/*
+ * V4L2 controls
+ */
+static int jpu_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct jpu_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->jpu->lock, flags);
+ if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY)
+ ctx->compr_quality = ctrl->val;
+ spin_unlock_irqrestore(&ctx->jpu->lock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops jpu_ctrl_ops = {
+ .s_ctrl = jpu_s_ctrl,
+};
+
+static int jpu_streamon(struct file *file, void *priv, enum v4l2_buf_type type)
+{
+ struct jpu_ctx *ctx = fh_to_ctx(priv);
+ struct jpu_q_data *src_q_data, *dst_q_data, *orig, adj, *ref;
+ enum v4l2_buf_type adj_type;
+
+ src_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ dst_q_data = jpu_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ if (ctx->encoder) {
+ adj = *src_q_data;
+ orig = src_q_data;
+ ref = dst_q_data;
+ adj_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ } else {
+ adj = *dst_q_data;
+ orig = dst_q_data;
+ ref = src_q_data;
+ adj_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ }
+
+ adj.format.width = ref->format.width;
+ adj.format.height = ref->format.height;
+
+ __jpu_try_fmt(ctx, NULL, &adj.format, adj_type);
+
+ if (adj.format.width != orig->format.width ||
+ adj.format.height != orig->format.height) {
+ dev_err(ctx->jpu->dev, "src and dst formats do not match.\n");
+ /* maybe we can return -EPIPE here? */
+ return -EINVAL;
+ }
+
+ return v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops jpu_ioctl_ops = {
+ .vidioc_querycap = jpu_querycap,
+
+ .vidioc_enum_fmt_vid_cap_mplane = jpu_enum_fmt_cap,
+ .vidioc_enum_fmt_vid_out_mplane = jpu_enum_fmt_out,
+ .vidioc_g_fmt_vid_cap_mplane = jpu_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = jpu_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = jpu_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = jpu_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = jpu_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = jpu_s_fmt,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = jpu_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe
+};
+
+static int jpu_controls_create(struct jpu_ctx *ctx)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
+
+ ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, &jpu_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, JPU_MAX_QUALITY - 1, 1, 0);
+
+ if (ctx->ctrl_handler.error) {
+ ret = ctx->ctrl_handler.error;
+ goto error_free;
+ }
+
+ if (!ctx->encoder)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
+
+ ret = v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (ret < 0)
+ goto error_free;
+
+ return 0;
+
+error_free:
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return ret;
+}
+
+/*
+ * ============================================================================
+ * Queue operations
+ * ============================================================================
+ */
+static int jpu_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
+ struct jpu_q_data *q_data;
+ unsigned int i;
+
+ q_data = jpu_get_q_data(ctx, vq->type);
+
+ *nplanes = q_data->format.num_planes;
+
+ for (i = 0; i < *nplanes; i++) {
+ unsigned int q_size = q_data->format.plane_fmt[i].sizeimage;
+ unsigned int f_size = fmt ?
+ fmt->fmt.pix_mp.plane_fmt[i].sizeimage : 0;
+
+ if (fmt && f_size < q_size)
+ return -EINVAL;
+
+ sizes[i] = fmt ? f_size : q_size;
+ alloc_ctxs[i] = ctx->jpu->alloc_ctx;
+ }
+
+ return 0;
+}
+
+static int jpu_buf_prepare(struct vb2_buffer *vb)
+{
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct jpu_q_data *q_data;
+ unsigned int i;
+
+ q_data = jpu_get_q_data(ctx, vb->vb2_queue->type);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vb->v4l2_buf.field == V4L2_FIELD_ANY)
+ vb->v4l2_buf.field = V4L2_FIELD_NONE;
+ if (vb->v4l2_buf.field != V4L2_FIELD_NONE) {
+ dev_err(ctx->jpu->dev, "%s field isn't supported\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < q_data->format.num_planes; i++) {
+ unsigned long size = q_data->format.plane_fmt[i].sizeimage;
+
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(ctx->jpu->dev,
+ "%s: data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ /* decoder capture queue */
+ if (!ctx->encoder && !V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static void jpu_buf_queue(struct vb2_buffer *vb)
+{
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (!ctx->encoder && V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vb);
+ struct jpu_q_data *q_data, adjust;
+ void *buffer = vb2_plane_vaddr(vb, 0);
+ unsigned long buf_size = vb2_get_plane_payload(vb, 0);
+ unsigned int width, height;
+
+ u8 subsampling = jpu_parse_hdr(buffer, buf_size, &width,
+ &height);
+
+ /* check if JPEG data basic parsing was successful */
+ if (subsampling != JPU_JPEG_422 && subsampling != JPU_JPEG_420)
+ goto format_error;
+
+ q_data = &ctx->out_q;
+
+ adjust = *q_data;
+ adjust.format.width = width;
+ adjust.format.height = height;
+
+ __jpu_try_fmt(ctx, &adjust.fmtinfo, &adjust.format,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ if (adjust.format.width != q_data->format.width ||
+ adjust.format.height != q_data->format.height)
+ goto format_error;
+
+ /*
+ * keep subsampling in buffer to check it
+ * for compatibility in device_run
+ */
+ jpu_buf->subsampling = subsampling;
+ }
+
+ if (ctx->fh.m2m_ctx)
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb);
+
+ return;
+
+format_error:
+ dev_err(ctx->jpu->dev, "incompatible or corrupted JPEG data\n");
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+static void jpu_buf_finish(struct vb2_buffer *vb)
+{
+ struct jpu_buffer *jpu_buf = vb2_to_jpu_buffer(vb);
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct jpu_q_data *q_data = &ctx->out_q;
+ enum v4l2_buf_type type = vb->vb2_queue->type;
+ u8 *buffer;
+
+ if (vb->state == VB2_BUF_STATE_DONE)
+ vb->v4l2_buf.sequence = jpu_get_q_data(ctx, type)->sequence++;
+
+ if (!ctx->encoder || vb->state != VB2_BUF_STATE_DONE ||
+ V4L2_TYPE_IS_OUTPUT(type))
+ return;
+
+ buffer = vb2_plane_vaddr(vb, 0);
+
+ memcpy(buffer, jpeg_hdrs[jpu_buf->compr_quality], JPU_JPEG_HDR_SIZE);
+ *(u16 *)(buffer + JPU_JPEG_HEIGHT_OFFSET) =
+ cpu_to_be16(q_data->format.height);
+ *(u16 *)(buffer + JPU_JPEG_WIDTH_OFFSET) =
+ cpu_to_be16(q_data->format.width);
+ *(buffer + JPU_JPEG_SUBS_OFFSET) = q_data->fmtinfo->subsampling;
+}
+
+static int jpu_start_streaming(struct vb2_queue *vq, unsigned count)
+{
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
+ struct jpu_q_data *q_data = jpu_get_q_data(ctx, vq->type);
+
+ q_data->sequence = 0;
+ return 0;
+}
+
+static void jpu_stop_streaming(struct vb2_queue *vq)
+{
+ struct jpu_ctx *ctx = vb2_get_drv_priv(vq);
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ for (;;) {
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vb = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ if (vb == NULL)
+ return;
+ spin_lock_irqsave(&ctx->jpu->lock, flags);
+ v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
+ spin_unlock_irqrestore(&ctx->jpu->lock, flags);
+ }
+}
+
+static struct vb2_ops jpu_qops = {
+ .queue_setup = jpu_queue_setup,
+ .buf_prepare = jpu_buf_prepare,
+ .buf_queue = jpu_buf_queue,
+ .buf_finish = jpu_buf_finish,
+ .start_streaming = jpu_start_streaming,
+ .stop_streaming = jpu_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int jpu_queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct jpu_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct jpu_buffer);
+ src_vq->ops = &jpu_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &ctx->jpu->mutex;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct jpu_buffer);
+ dst_vq->ops = &jpu_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &ctx->jpu->mutex;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * ============================================================================
+ * Device file operations
+ * ============================================================================
+ */
+static int jpu_open(struct file *file)
+{
+ struct jpu *jpu = video_drvdata(file);
+ struct video_device *vfd = video_devdata(file);
+ struct jpu_ctx *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ v4l2_fh_init(&ctx->fh, vfd);
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->jpu = jpu;
+ ctx->encoder = vfd == &jpu->vfd_encoder;
+
+ __jpu_try_fmt(ctx, &ctx->out_q.fmtinfo, &ctx->out_q.format,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ __jpu_try_fmt(ctx, &ctx->cap_q.fmtinfo, &ctx->cap_q.format,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(jpu->m2m_dev, ctx, jpu_queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto v4l_prepare_rollback;
+ }
+
+ ret = jpu_controls_create(ctx);
+ if (ret < 0)
+ goto v4l_prepare_rollback;
+
+ if (mutex_lock_interruptible(&jpu->mutex)) {
+ ret = -ERESTARTSYS;
+ goto v4l_prepare_rollback;
+ }
+
+ if (jpu->ref_count == 0) {
+ ret = clk_prepare_enable(jpu->clk);
+ if (ret < 0)
+ goto device_prepare_rollback;
+ /* ...issue software reset */
+ ret = jpu_reset(jpu);
+ if (ret)
+ goto device_prepare_rollback;
+ }
+
+ jpu->ref_count++;
+
+ mutex_unlock(&jpu->mutex);
+ return 0;
+
+device_prepare_rollback:
+ mutex_unlock(&jpu->mutex);
+v4l_prepare_rollback:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
+}
+
+static int jpu_release(struct file *file)
+{
+ struct jpu *jpu = video_drvdata(file);
+ struct jpu_ctx *ctx = fh_to_ctx(file->private_data);
+
+ mutex_lock(&jpu->mutex);
+ if (--jpu->ref_count == 0)
+ clk_disable_unprepare(jpu->clk);
+ mutex_unlock(&jpu->mutex);
+
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations jpu_fops = {
+ .owner = THIS_MODULE,
+ .open = jpu_open,
+ .release = jpu_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+/*
+ * ============================================================================
+ * mem2mem callbacks
+ * ============================================================================
+ */
+static void jpu_cleanup(struct jpu_ctx *ctx, bool reset)
+{
+ /* remove current buffers and finish job */
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->jpu->lock, flags);
+
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+
+ /* ...and give it a chance on next run */
+ if (reset)
+ jpu_write(ctx->jpu, JCCMD_SRST, JCCMD);
+
+ spin_unlock_irqrestore(&ctx->jpu->lock, flags);
+
+ v4l2_m2m_job_finish(ctx->jpu->m2m_dev, ctx->fh.m2m_ctx);
+}
+
+static void jpu_device_run(void *priv)
+{
+ struct jpu_ctx *ctx = priv;
+ struct jpu *jpu = ctx->jpu;
+ struct jpu_buffer *jpu_buf;
+ struct jpu_q_data *q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned int w, h, bpl;
+ unsigned char num_planes, subsampling;
+ unsigned long flags;
+
+ /* ...wait until module reset completes; we have mutex locked here */
+ if (jpu_wait_reset(jpu)) {
+ jpu_cleanup(ctx, true);
+ return;
+ }
+
+ spin_lock_irqsave(&ctx->jpu->lock, flags);
+
+ jpu->curr = ctx;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+
+ if (ctx->encoder) {
+ jpu_buf = vb2_to_jpu_buffer(dst_buf);
+ q_data = &ctx->out_q;
+ } else {
+ jpu_buf = vb2_to_jpu_buffer(src_buf);
+ q_data = &ctx->cap_q;
+ }
+
+ w = q_data->format.width;
+ h = q_data->format.height;
+ bpl = q_data->format.plane_fmt[0].bytesperline;
+ num_planes = q_data->fmtinfo->num_planes;
+ subsampling = q_data->fmtinfo->subsampling;
+
+ if (ctx->encoder) {
+ unsigned long src_1_addr, src_2_addr, dst_addr;
+ unsigned int redu, inft;
+
+ dst_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ src_1_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ if (num_planes > 1)
+ src_2_addr = vb2_dma_contig_plane_dma_addr(src_buf, 1);
+ else
+ src_2_addr = src_1_addr + w * h;
+
+ jpu_buf->compr_quality = ctx->compr_quality;
+
+ if (subsampling == JPU_JPEG_420) {
+ redu = JCMOD_REDU_420;
+ inft = JIFECNT_INFT_420;
+ } else {
+ redu = JCMOD_REDU_422;
+ inft = JIFECNT_INFT_422;
+ }
+
+ /* only no marker mode works for encoding */
+ jpu_write(jpu, JCMOD_DSP_ENC | JCMOD_PCTR | redu |
+ JCMOD_MSKIP_ENABLE, JCMOD);
+
+ jpu_write(jpu, JIFECNT_SWAP_WB | inft, JIFECNT);
+ jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT);
+ jpu_write(jpu, JINTE_TRANSF_COMPL, JINTE);
+
+ /* Y and C components source addresses */
+ jpu_write(jpu, src_1_addr, JIFESYA1);
+ jpu_write(jpu, src_2_addr, JIFESCA1);
+
+ /* memory width */
+ jpu_write(jpu, bpl, JIFESMW);
+
+ jpu_write(jpu, (w >> 8) & JCSZ_MASK, JCHSZU);
+ jpu_write(jpu, w & JCSZ_MASK, JCHSZD);
+
+ jpu_write(jpu, (h >> 8) & JCSZ_MASK, JCVSZU);
+ jpu_write(jpu, h & JCSZ_MASK, JCVSZD);
+
+ jpu_write(jpu, w, JIFESHSZ);
+ jpu_write(jpu, h, JIFESVSZ);
+
+ jpu_write(jpu, dst_addr + JPU_JPEG_HDR_SIZE, JIFEDA1);
+
+ jpu_write(jpu, 0 << JCQTN_SHIFT(1) | 1 << JCQTN_SHIFT(2) |
+ 1 << JCQTN_SHIFT(3), JCQTN);
+
+ jpu_write(jpu, 0 << JCHTN_AC_SHIFT(1) | 0 << JCHTN_DC_SHIFT(1) |
+ 1 << JCHTN_AC_SHIFT(2) | 1 << JCHTN_DC_SHIFT(2) |
+ 1 << JCHTN_AC_SHIFT(3) | 1 << JCHTN_DC_SHIFT(3),
+ JCHTN);
+
+ jpu_set_qtbl(jpu, ctx->compr_quality);
+ jpu_set_htbl(jpu);
+ } else {
+ unsigned long src_addr, dst_1_addr, dst_2_addr;
+
+ if (jpu_buf->subsampling != subsampling) {
+ dev_err(ctx->jpu->dev,
+ "src and dst formats do not match.\n");
+ spin_unlock_irqrestore(&ctx->jpu->lock, flags);
+ jpu_cleanup(ctx, false);
+ return;
+ }
+
+ src_addr = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ dst_1_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (q_data->fmtinfo->num_planes > 1)
+ dst_2_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 1);
+ else
+ dst_2_addr = dst_1_addr + w * h;
+
+ /* ...set up decoder operation */
+ jpu_write(jpu, JCMOD_DSP_DEC | JCMOD_PCTR, JCMOD);
+ jpu_write(jpu, JIFECNT_SWAP_WB, JIFECNT);
+ jpu_write(jpu, JIFDCNT_SWAP_WB, JIFDCNT);
+
+ /* ...enable interrupts on transfer completion and d-g error */
+ jpu_write(jpu, JINTE_TRANSF_COMPL | JINTE_ERR, JINTE);
+
+ /* ...set source/destination addresses of encoded data */
+ jpu_write(jpu, src_addr, JIFDSA1);
+ jpu_write(jpu, dst_1_addr, JIFDDYA1);
+ jpu_write(jpu, dst_2_addr, JIFDDCA1);
+
+ jpu_write(jpu, bpl, JIFDDMW);
+ }
+
+ /* ...start encoder/decoder operation */
+ jpu_write(jpu, JCCMD_JSRT, JCCMD);
+
+ spin_unlock_irqrestore(&ctx->jpu->lock, flags);
+}
+
+static int jpu_job_ready(void *priv)
+{
+ return 1;
+}
+
+static void jpu_job_abort(void *priv)
+{
+ struct jpu_ctx *ctx = priv;
+
+ if (!wait_event_timeout(ctx->jpu->irq_queue, !ctx->jpu->curr,
+ msecs_to_jiffies(JPU_JOB_TIMEOUT)))
+ jpu_cleanup(ctx, true);
+}
+
+static struct v4l2_m2m_ops jpu_m2m_ops = {
+ .device_run = jpu_device_run,
+ .job_ready = jpu_job_ready,
+ .job_abort = jpu_job_abort,
+};
+
+/*
+ * ============================================================================
+ * IRQ handler
+ * ============================================================================
+ */
+static irqreturn_t jpu_irq_handler(int irq, void *dev_id)
+{
+ struct jpu *jpu = dev_id;
+ struct jpu_ctx *curr_ctx;
+ struct vb2_buffer *src_buf, *dst_buf;
+ unsigned int int_status;
+
+ int_status = jpu_read(jpu, JINTS);
+
+ /* ...spurious interrupt */
+ if (!((JINTS_TRANSF_COMPL | JINTS_PROCESS_COMPL | JINTS_ERR) &
+ int_status))
+ return IRQ_NONE;
+
+ /* ...clear interrupts */
+ jpu_write(jpu, ~(int_status & JINTS_MASK), JINTS);
+ if (int_status & (JINTS_ERR | JINTS_PROCESS_COMPL))
+ jpu_write(jpu, JCCMD_JEND, JCCMD);
+
+ spin_lock(&jpu->lock);
+
+ if ((int_status & JINTS_PROCESS_COMPL) &&
+ !(int_status & JINTS_TRANSF_COMPL))
+ goto handled;
+
+ curr_ctx = v4l2_m2m_get_curr_priv(jpu->m2m_dev);
+ if (!curr_ctx) {
+ /* ...instance is not running */
+ dev_err(jpu->dev, "no active context for m2m\n");
+ goto handled;
+ }
+
+ src_buf = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx);
+ dst_buf = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx);
+
+ if (int_status & JINTS_TRANSF_COMPL) {
+ if (curr_ctx->encoder) {
+ unsigned long payload_size = jpu_read(jpu, JCDTCU) << 16
+ | jpu_read(jpu, JCDTCM) << 8
+ | jpu_read(jpu, JCDTCD);
+ vb2_set_plane_payload(dst_buf, 0,
+ payload_size + JPU_JPEG_HDR_SIZE);
+ }
+
+ dst_buf->v4l2_buf.field = src_buf->v4l2_buf.field;
+ dst_buf->v4l2_buf.timestamp = src_buf->v4l2_buf.timestamp;
+ if (src_buf->v4l2_buf.flags & V4L2_BUF_FLAG_TIMECODE)
+ dst_buf->v4l2_buf.timecode = src_buf->v4l2_buf.timecode;
+ dst_buf->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags |= src_buf->v4l2_buf.flags &
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst_buf->v4l2_buf.flags = src_buf->v4l2_buf.flags &
+ (V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_KEYFRAME |
+ V4L2_BUF_FLAG_PFRAME | V4L2_BUF_FLAG_BFRAME |
+ V4L2_BUF_FLAG_TSTAMP_SRC_MASK);
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+ } else if (int_status & JINTS_ERR) {
+ unsigned char error = jpu_read(jpu, JCDERR) & JCDERR_MASK;
+
+ dev_dbg(jpu->dev, "processing error: %#X: %s\n", error,
+ error_to_text[error]);
+
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ }
+
+ jpu->curr = NULL;
+
+ /* ...reset JPU after completion */
+ jpu_write(jpu, JCCMD_SRST, JCCMD);
+ spin_unlock(&jpu->lock);
+
+ v4l2_m2m_job_finish(jpu->m2m_dev, curr_ctx->fh.m2m_ctx);
+
+ /* ...wakeup abort routine if needed */
+ wake_up(&jpu->irq_queue);
+
+ return IRQ_HANDLED;
+
+handled:
+ spin_unlock(&jpu->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * ============================================================================
+ * Driver basic infrastructure
+ * ============================================================================
+ */
+static const struct of_device_id jpu_dt_ids[] = {
+ { .compatible = "renesas,jpu-r8a7790" }, /* H2 */
+ { .compatible = "renesas,jpu-r8a7791" }, /* M2-W */
+ { .compatible = "renesas,jpu-r8a7792" }, /* V2H */
+ { .compatible = "renesas,jpu-r8a7793" }, /* M2-N */
+ { },
+};
+MODULE_DEVICE_TABLE(of, jpu_dt_ids);
+
+static int jpu_probe(struct platform_device *pdev)
+{
+ struct jpu *jpu;
+ struct resource *res;
+ int ret;
+ unsigned int i;
+
+ jpu = devm_kzalloc(&pdev->dev, sizeof(*jpu), GFP_KERNEL);
+ if (!jpu)
+ return -ENOMEM;
+
+ init_waitqueue_head(&jpu->irq_queue);
+ mutex_init(&jpu->mutex);
+ spin_lock_init(&jpu->lock);
+ jpu->dev = &pdev->dev;
+
+ /* memory-mapped registers */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ jpu->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(jpu->regs))
+ return PTR_ERR(jpu->regs);
+
+ /* interrupt service routine registration */
+ jpu->irq = ret = platform_get_irq(pdev, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot find IRQ\n");
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, jpu->irq, jpu_irq_handler, 0,
+ dev_name(&pdev->dev), jpu);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpu->irq);
+ return ret;
+ }
+
+ /* clocks */
+ jpu->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(jpu->clk)) {
+ dev_err(&pdev->dev, "cannot get clock\n");
+ return PTR_ERR(jpu->clk);
+ }
+
+ /* v4l2 device */
+ ret = v4l2_device_register(&pdev->dev, &jpu->v4l2_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register v4l2 device\n");
+ return ret;
+ }
+
+ /* mem2mem device */
+ jpu->m2m_dev = v4l2_m2m_init(&jpu_m2m_ops);
+ if (IS_ERR(jpu->m2m_dev)) {
+ v4l2_err(&jpu->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(jpu->m2m_dev);
+ goto device_register_rollback;
+ }
+
+ jpu->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(jpu->alloc_ctx)) {
+ v4l2_err(&jpu->v4l2_dev, "Failed to init memory allocator\n");
+ ret = PTR_ERR(jpu->alloc_ctx);
+ goto m2m_init_rollback;
+ }
+
+ /* fill in qantization and Huffman tables for encoder */
+ for (i = 0; i < JPU_MAX_QUALITY; i++)
+ jpu_generate_hdr(i, (unsigned char *)jpeg_hdrs[i]);
+
+ strlcpy(jpu->vfd_encoder.name, DRV_NAME, sizeof(jpu->vfd_encoder.name));
+ jpu->vfd_encoder.fops = &jpu_fops;
+ jpu->vfd_encoder.ioctl_ops = &jpu_ioctl_ops;
+ jpu->vfd_encoder.minor = -1;
+ jpu->vfd_encoder.release = video_device_release_empty;
+ jpu->vfd_encoder.lock = &jpu->mutex;
+ jpu->vfd_encoder.v4l2_dev = &jpu->v4l2_dev;
+ jpu->vfd_encoder.vfl_dir = VFL_DIR_M2M;
+
+ ret = video_register_device(&jpu->vfd_encoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
+ goto vb2_allocator_rollback;
+ }
+
+ video_set_drvdata(&jpu->vfd_encoder, jpu);
+
+ strlcpy(jpu->vfd_decoder.name, DRV_NAME, sizeof(jpu->vfd_decoder.name));
+ jpu->vfd_decoder.fops = &jpu_fops;
+ jpu->vfd_decoder.ioctl_ops = &jpu_ioctl_ops;
+ jpu->vfd_decoder.minor = -1;
+ jpu->vfd_decoder.release = video_device_release_empty;
+ jpu->vfd_decoder.lock = &jpu->mutex;
+ jpu->vfd_decoder.v4l2_dev = &jpu->v4l2_dev;
+ jpu->vfd_decoder.vfl_dir = VFL_DIR_M2M;
+
+ ret = video_register_device(&jpu->vfd_decoder, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&jpu->v4l2_dev, "Failed to register video device\n");
+ goto enc_vdev_register_rollback;
+ }
+
+ video_set_drvdata(&jpu->vfd_decoder, jpu);
+ platform_set_drvdata(pdev, jpu);
+
+ v4l2_info(&jpu->v4l2_dev, "encoder device registered as /dev/video%d\n",
+ jpu->vfd_encoder.num);
+ v4l2_info(&jpu->v4l2_dev, "decoder device registered as /dev/video%d\n",
+ jpu->vfd_decoder.num);
+
+ return 0;
+
+enc_vdev_register_rollback:
+ video_unregister_device(&jpu->vfd_encoder);
+
+vb2_allocator_rollback:
+ vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx);
+
+m2m_init_rollback:
+ v4l2_m2m_release(jpu->m2m_dev);
+
+device_register_rollback:
+ v4l2_device_unregister(&jpu->v4l2_dev);
+
+ return ret;
+}
+
+static int jpu_remove(struct platform_device *pdev)
+{
+ struct jpu *jpu = platform_get_drvdata(pdev);
+
+ video_unregister_device(&jpu->vfd_decoder);
+ video_unregister_device(&jpu->vfd_encoder);
+ vb2_dma_contig_cleanup_ctx(jpu->alloc_ctx);
+ v4l2_m2m_release(jpu->m2m_dev);
+ v4l2_device_unregister(&jpu->v4l2_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int jpu_suspend(struct device *dev)
+{
+ struct jpu *jpu = dev_get_drvdata(dev);
+
+ if (jpu->ref_count == 0)
+ return 0;
+
+ clk_disable_unprepare(jpu->clk);
+
+ return 0;
+}
+
+static int jpu_resume(struct device *dev)
+{
+ struct jpu *jpu = dev_get_drvdata(dev);
+
+ if (jpu->ref_count == 0)
+ return 0;
+
+ clk_prepare_enable(jpu->clk);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops jpu_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(jpu_suspend, jpu_resume)
+};
+
+static struct platform_driver jpu_driver = {
+ .probe = jpu_probe,
+ .remove = jpu_remove,
+ .driver = {
+ .of_match_table = jpu_dt_ids,
+ .name = DRV_NAME,
+ .pm = &jpu_pm_ops,
+ },
+};
+
+module_platform_driver(jpu_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_AUTHOR("Mikhail Ulianov <mikhail.ulyanov@cogentembedded.com>");
+MODULE_DESCRIPTION("Renesas R-Car JPEG processing unit driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/s5p-jpeg/jpeg-core.c
index bfbf15756..9690f9dcb 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/s5p-jpeg/jpeg-core.c
@@ -2544,7 +2544,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1);
if (ret) {
v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
- goto enc_vdev_alloc_rollback;
+ video_device_release(jpeg->vfd_encoder);
+ goto vb2_allocator_rollback;
}
video_set_drvdata(jpeg->vfd_encoder, jpeg);
@@ -2572,7 +2573,8 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1);
if (ret) {
v4l2_err(&jpeg->v4l2_dev, "Failed to register video device\n");
- goto dec_vdev_alloc_rollback;
+ video_device_release(jpeg->vfd_decoder);
+ goto enc_vdev_register_rollback;
}
video_set_drvdata(jpeg->vfd_decoder, jpeg);
@@ -2589,15 +2591,9 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
return 0;
-dec_vdev_alloc_rollback:
- video_device_release(jpeg->vfd_decoder);
-
enc_vdev_register_rollback:
video_unregister_device(jpeg->vfd_encoder);
-enc_vdev_alloc_rollback:
- video_device_release(jpeg->vfd_encoder);
-
vb2_allocator_rollback:
vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
@@ -2622,9 +2618,7 @@ static int s5p_jpeg_remove(struct platform_device *pdev)
pm_runtime_disable(jpeg->dev);
video_unregister_device(jpeg->vfd_decoder);
- video_device_release(jpeg->vfd_decoder);
video_unregister_device(jpeg->vfd_encoder);
- video_device_release(jpeg->vfd_encoder);
vb2_dma_contig_cleanup_ctx(jpeg->alloc_ctx);
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
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 f17609669..b1b149151 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -37,8 +37,12 @@ static int s5p_mfc_sys_init_cmd_v6(struct s5p_mfc_dev *dev)
{
struct s5p_mfc_cmd_args h2r_args;
struct s5p_mfc_buf_size_v6 *buf_size = dev->variant->buf_size->priv;
+ int ret;
+
+ ret = s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev);
+ if (ret)
+ return ret;
- s5p_mfc_hw_call(dev->mfc_ops, alloc_dev_context_buffer, dev);
mfc_write(dev, dev->ctx_buf.dma, S5P_FIMV_CONTEXT_MEM_ADDR_V6);
mfc_write(dev, buf_size->dev_ctx, S5P_FIMV_CONTEXT_MEM_SIZE_V6);
return s5p_mfc_cmd_host2risc_v6(dev, S5P_FIMV_H2R_CMD_SYS_INIT_V6,
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
index e65993f4b..2e57e9f45 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
@@ -1819,11 +1819,12 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq,
struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv);
struct s5p_mfc_dev *dev = ctx->dev;
- if (ctx->state != MFCINST_GOT_INST) {
- mfc_err("inavlid state: %d\n", ctx->state);
- return -EINVAL;
- }
if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (ctx->state != MFCINST_GOT_INST) {
+ mfc_err("inavlid state: %d\n", ctx->state);
+ return -EINVAL;
+ }
+
if (ctx->dst_fmt)
*plane_count = ctx->dst_fmt->num_planes;
else
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
index 00a1d8b2a..1e7250260 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
@@ -37,10 +37,9 @@ void s5p_mfc_init_regs(struct s5p_mfc_dev *dev)
dev->mfc_regs = s5p_mfc_init_regs_v6_plus(dev);
}
-int s5p_mfc_alloc_priv_buf(struct device *dev,
+int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base,
struct s5p_mfc_priv_buf *b)
{
-
mfc_debug(3, "Allocating priv: %zu\n", b->size);
b->virt = dma_alloc_coherent(dev, b->size, &b->dma, GFP_KERNEL);
@@ -50,6 +49,14 @@ int s5p_mfc_alloc_priv_buf(struct device *dev,
return -ENOMEM;
}
+ if (b->dma < base) {
+ mfc_err("Invaling memory configuration!\n");
+ mfc_err("Allocated buffer (%pad) is lower than memory base address (%pad)\n",
+ &b->dma, &base);
+ dma_free_coherent(dev, b->size, b->virt, b->dma);
+ return -ENOMEM;
+ }
+
mfc_debug(3, "Allocated addr %p %pad\n", b->virt, &b->dma);
return 0;
}
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
index 22dfb3eff..77a08b19b 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
@@ -334,7 +334,7 @@ struct s5p_mfc_hw_ops {
void s5p_mfc_init_hw_ops(struct s5p_mfc_dev *dev);
void s5p_mfc_init_regs(struct s5p_mfc_dev *dev);
-int s5p_mfc_alloc_priv_buf(struct device *dev,
+int s5p_mfc_alloc_priv_buf(struct device *dev, dma_addr_t base,
struct s5p_mfc_priv_buf *b);
void s5p_mfc_release_priv_buf(struct device *dev,
struct s5p_mfc_priv_buf *b);
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
index 9a923b1a9..6402f76cc 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
@@ -41,7 +41,7 @@ static int s5p_mfc_alloc_dec_temp_buffers_v5(struct s5p_mfc_ctx *ctx)
int ret;
ctx->dsc.size = buf_size->dsc;
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->dsc);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->dsc);
if (ret) {
mfc_err("Failed to allocate temporary buffer\n");
return ret;
@@ -172,7 +172,8 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
/* Allocate only if memory from bank 1 is necessary */
if (ctx->bank1.size > 0) {
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1,
+ &ctx->bank1);
if (ret) {
mfc_err("Failed to allocate Bank1 temporary buffer\n");
return ret;
@@ -181,7 +182,8 @@ static int s5p_mfc_alloc_codec_buffers_v5(struct s5p_mfc_ctx *ctx)
}
/* Allocate only if memory from bank 2 is necessary */
if (ctx->bank2.size > 0) {
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, &ctx->bank2);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_r, dev->bank2,
+ &ctx->bank2);
if (ret) {
mfc_err("Failed to allocate Bank2 temporary buffer\n");
s5p_mfc_release_priv_buf(ctx->dev->mem_dev_l, &ctx->bank1);
@@ -212,7 +214,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
else
ctx->ctx.size = buf_size->non_h264_ctx;
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx);
if (ret) {
mfc_err("Failed to allocate instance buffer\n");
return ret;
@@ -225,7 +227,7 @@ static int s5p_mfc_alloc_instance_buffer_v5(struct s5p_mfc_ctx *ctx)
/* Initialize shared memory */
ctx->shm.size = buf_size->shm;
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->shm);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->shm);
if (ret) {
mfc_err("Failed to allocate shared memory buffer\n");
s5p_mfc_release_priv_buf(dev->mem_dev_l, &ctx->ctx);
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 12497f5ed..e5cb30e1f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
@@ -239,7 +239,8 @@ static int s5p_mfc_alloc_codec_buffers_v6(struct s5p_mfc_ctx *ctx)
/* Allocate only if memory from bank 1 is necessary */
if (ctx->bank1.size > 0) {
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->bank1);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1,
+ &ctx->bank1);
if (ret) {
mfc_err("Failed to allocate Bank1 memory\n");
return ret;
@@ -291,7 +292,7 @@ static int s5p_mfc_alloc_instance_buffer_v6(struct s5p_mfc_ctx *ctx)
break;
}
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &ctx->ctx);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1, &ctx->ctx);
if (ret) {
mfc_err("Failed to allocate instance buffer\n");
return ret;
@@ -320,7 +321,8 @@ static int s5p_mfc_alloc_dev_context_buffer_v6(struct s5p_mfc_dev *dev)
mfc_debug_enter();
dev->ctx_buf.size = buf_size->dev_ctx;
- ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, &dev->ctx_buf);
+ ret = s5p_mfc_alloc_priv_buf(dev->mem_dev_l, dev->bank1,
+ &dev->ctx_buf);
if (ret) {
mfc_err("Failed to allocate device context buffer\n");
return ret;
@@ -1734,7 +1736,7 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
mfc_debug(1, "New context: %d\n", new_ctx);
ctx = dev->ctx[new_ctx];
- mfc_debug(1, "Seting new context to %p\n", ctx);
+ mfc_debug(1, "Setting new context to %p\n", ctx);
/* Got context to run in ctx */
mfc_debug(1, "ctx->dst_queue_cnt=%d ctx->dpb_count=%d ctx->src_queue_cnt=%d\n",
ctx->dst_queue_cnt, ctx->pb_count, ctx->src_queue_cnt);
diff --git a/drivers/media/platform/s5p-tv/hdmiphy_drv.c b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
index c2f2e3564..aae652351 100644
--- a/drivers/media/platform/s5p-tv/hdmiphy_drv.c
+++ b/drivers/media/platform/s5p-tv/hdmiphy_drv.c
@@ -315,7 +315,6 @@ MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
static struct i2c_driver hdmiphy_driver = {
.driver = {
.name = "s5p-hdmiphy",
- .owner = THIS_MODULE,
},
.probe = hdmiphy_probe,
.remove = hdmiphy_remove,
diff --git a/drivers/media/platform/s5p-tv/mixer_reg.c b/drivers/media/platform/s5p-tv/mixer_reg.c
index b71340302..5127acb1e 100644
--- a/drivers/media/platform/s5p-tv/mixer_reg.c
+++ b/drivers/media/platform/s5p-tv/mixer_reg.c
@@ -357,17 +357,15 @@ void mxr_reg_streamoff(struct mxr_device *mdev)
int mxr_reg_wait4vsync(struct mxr_device *mdev)
{
- int ret;
+ long time_left;
clear_bit(MXR_EVENT_VSYNC, &mdev->event_flags);
/* TODO: consider adding interruptible */
- ret = wait_event_timeout(mdev->event_queue,
- test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
- msecs_to_jiffies(1000));
- if (ret > 0)
+ time_left = wait_event_timeout(mdev->event_queue,
+ test_bit(MXR_EVENT_VSYNC, &mdev->event_flags),
+ msecs_to_jiffies(1000));
+ if (time_left > 0)
return 0;
- if (ret < 0)
- return ret;
mxr_warn(mdev, "no vsync detected - timeout\n");
return -ETIME;
}
diff --git a/drivers/media/platform/s5p-tv/sii9234_drv.c b/drivers/media/platform/s5p-tv/sii9234_drv.c
index db8c17bb4..8d171310a 100644
--- a/drivers/media/platform/s5p-tv/sii9234_drv.c
+++ b/drivers/media/platform/s5p-tv/sii9234_drv.c
@@ -397,7 +397,6 @@ MODULE_DEVICE_TABLE(i2c, sii9234_id);
static struct i2c_driver sii9234_driver = {
.driver = {
.name = "sii9234",
- .owner = THIS_MODULE,
.pm = &sii9234_pm_ops,
},
.probe = sii9234_probe,
diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c
index 2554f3719..f5e3eb3a2 100644
--- a/drivers/media/platform/sh_veu.c
+++ b/drivers/media/platform/sh_veu.c
@@ -211,7 +211,7 @@ static enum v4l2_colorspace sh_veu_4cc2cspace(u32 fourcc)
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV24:
- return V4L2_COLORSPACE_JPEG;
+ return V4L2_COLORSPACE_SMPTE170M;
case V4L2_PIX_FMT_RGB332:
case V4L2_PIX_FMT_RGB444:
case V4L2_PIX_FMT_RGB565:
@@ -958,6 +958,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
src_vq->ops = &sh_veu_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->lock = &veu->fop_lock;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
ret = vb2_queue_init(src_vq);
if (ret < 0)
@@ -971,6 +972,7 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq,
dst_vq->ops = &sh_veu_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->lock = &veu->fop_lock;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
return vb2_queue_init(dst_vq);
}
@@ -1103,6 +1105,12 @@ static irqreturn_t sh_veu_isr(int irq, void *dev_id)
if (!src || !dst)
return IRQ_NONE;
+ dst->v4l2_buf.timestamp = src->v4l2_buf.timestamp;
+ dst->v4l2_buf.flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst->v4l2_buf.flags |=
+ src->v4l2_buf.flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ dst->v4l2_buf.timecode = src->v4l2_buf.timecode;
+
spin_lock(&veu->lock);
v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c
index 8b799bae0..fe5c8ab06 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/sh_vou.c
@@ -27,7 +27,7 @@
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mediabus.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-dma-contig.h>
/* Mirror addresses are not available for all registers */
#define VOUER 0
@@ -57,31 +57,40 @@ enum sh_vou_status {
SH_VOU_RUNNING,
};
+#define VOU_MIN_IMAGE_WIDTH 16
#define VOU_MAX_IMAGE_WIDTH 720
-#define VOU_MAX_IMAGE_HEIGHT 576
+#define VOU_MIN_IMAGE_HEIGHT 16
+
+struct sh_vou_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+static inline struct sh_vou_buffer *to_sh_vou_buffer(struct vb2_buffer *vb2)
+{
+ return container_of(vb2, struct sh_vou_buffer, vb);
+}
struct sh_vou_device {
struct v4l2_device v4l2_dev;
struct video_device vdev;
- atomic_t use_count;
struct sh_vou_pdata *pdata;
spinlock_t lock;
void __iomem *base;
/* State information */
struct v4l2_pix_format pix;
struct v4l2_rect rect;
- struct list_head queue;
+ struct list_head buf_list;
v4l2_std_id std;
int pix_idx;
- struct videobuf_buffer *active;
+ struct vb2_queue queue;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct sh_vou_buffer *active;
enum sh_vou_status status;
+ unsigned sequence;
struct mutex fop_lock;
};
-struct sh_vou_file {
- struct videobuf_queue vbq;
-};
-
/* Register access routines for sides A, B and mirror addresses */
static void sh_vou_reg_a_write(struct sh_vou_device *vou_dev, unsigned int reg,
u32 value)
@@ -133,6 +142,7 @@ struct sh_vou_fmt {
u32 pfmt;
char *desc;
unsigned char bpp;
+ unsigned char bpl;
unsigned char rgb;
unsigned char yf;
unsigned char pkf;
@@ -143,6 +153,7 @@ static struct sh_vou_fmt vou_fmt[] = {
{
.pfmt = V4L2_PIX_FMT_NV12,
.bpp = 12,
+ .bpl = 1,
.desc = "YVU420 planar",
.yf = 0,
.rgb = 0,
@@ -150,6 +161,7 @@ static struct sh_vou_fmt vou_fmt[] = {
{
.pfmt = V4L2_PIX_FMT_NV16,
.bpp = 16,
+ .bpl = 1,
.desc = "YVYU planar",
.yf = 1,
.rgb = 0,
@@ -157,6 +169,7 @@ static struct sh_vou_fmt vou_fmt[] = {
{
.pfmt = V4L2_PIX_FMT_RGB24,
.bpp = 24,
+ .bpl = 3,
.desc = "RGB24",
.pkf = 2,
.rgb = 1,
@@ -164,6 +177,7 @@ static struct sh_vou_fmt vou_fmt[] = {
{
.pfmt = V4L2_PIX_FMT_RGB565,
.bpp = 16,
+ .bpl = 2,
.desc = "RGB565",
.pkf = 3,
.rgb = 1,
@@ -171,6 +185,7 @@ static struct sh_vou_fmt vou_fmt[] = {
{
.pfmt = V4L2_PIX_FMT_RGB565X,
.bpp = 16,
+ .bpl = 2,
.desc = "RGB565 byteswapped",
.pkf = 3,
.rgb = 1,
@@ -178,11 +193,11 @@ static struct sh_vou_fmt vou_fmt[] = {
};
static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
- struct videobuf_buffer *vb)
+ struct vb2_buffer *vb)
{
dma_addr_t addr1, addr2;
- addr1 = videobuf_to_dma_contig(vb);
+ addr1 = vb2_dma_contig_plane_dma_addr(vb, 0);
switch (vou_dev->pix.pixelformat) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
@@ -196,8 +211,7 @@ static void sh_vou_schedule_next(struct sh_vou_device *vou_dev,
sh_vou_reg_m_write(vou_dev, VOUAD2R, addr2);
}
-static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
- struct videobuf_buffer *vb)
+static void sh_vou_stream_config(struct sh_vou_device *vou_dev)
{
unsigned int row_coeff;
#ifdef __LITTLE_ENDIAN
@@ -224,167 +238,136 @@ static void sh_vou_stream_start(struct sh_vou_device *vou_dev,
sh_vou_reg_a_write(vou_dev, VOUSWR, dataswap);
sh_vou_reg_ab_write(vou_dev, VOUAIR, vou_dev->pix.width * row_coeff);
- sh_vou_schedule_next(vou_dev, vb);
-}
-
-static void free_buffer(struct videobuf_queue *vq, struct videobuf_buffer *vb)
-{
- BUG_ON(in_interrupt());
-
- /* Wait until this buffer is no longer in STATE_QUEUED or STATE_ACTIVE */
- videobuf_waiton(vq, vb, 0, 0);
- videobuf_dma_contig_free(vq, vb);
- vb->state = VIDEOBUF_NEEDS_INIT;
}
/* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int sh_vou_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
-
- *size = vou_fmt[vou_dev->pix_idx].bpp * vou_dev->pix.width *
- vou_dev->pix.height / 8;
-
- if (*count < 2)
- *count = 2;
-
- /* Taking into account maximum frame size, *count will stay >= 2 */
- if (PAGE_ALIGN(*size) * *count > 4 * 1024 * 1024)
- *count = 4 * 1024 * 1024 / PAGE_ALIGN(*size);
+ struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+ struct v4l2_pix_format *pix = &vou_dev->pix;
+ int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): count=%d, size=%d\n", __func__,
- *count, *size);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+ if (fmt && fmt->fmt.pix.sizeimage < pix->height * bytes_per_line)
+ return -EINVAL;
+ *nplanes = 1;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : pix->height * bytes_per_line;
+ alloc_ctxs[0] = vou_dev->alloc_ctx;
return 0;
}
-/* Locking: caller holds fop_lock mutex */
-static int sh_vou_buf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
+static int sh_vou_buf_prepare(struct vb2_buffer *vb)
{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);
struct v4l2_pix_format *pix = &vou_dev->pix;
- int bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
- int ret;
+ unsigned bytes_per_line = vou_fmt[vou_dev->pix_idx].bpp * pix->width / 8;
+ unsigned size = pix->height * bytes_per_line;
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
- if (vb->width != pix->width ||
- vb->height != pix->height ||
- vb->field != pix->field) {
- vb->width = pix->width;
- vb->height = pix->height;
- vb->field = field;
- if (vb->state != VIDEOBUF_NEEDS_INIT)
- free_buffer(vq, vb);
- }
-
- vb->size = vb->height * bytes_per_line;
- if (vb->baddr && vb->bsize < vb->size) {
+ if (vb2_plane_size(vb, 0) < size) {
/* User buffer too small */
- dev_warn(vq->dev, "User buffer too small: [%zu] @ %lx\n",
- vb->bsize, vb->baddr);
+ dev_warn(vou_dev->v4l2_dev.dev, "buffer too small (%lu < %u)\n",
+ vb2_plane_size(vb, 0), size);
return -EINVAL;
}
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret < 0) {
- dev_warn(vq->dev, "IOLOCK buf-type %d: %d\n",
- vb->memory, ret);
- return ret;
- }
- vb->state = VIDEOBUF_PREPARED;
- }
-
- dev_dbg(vou_dev->v4l2_dev.dev,
- "%s(): fmt #%d, %u bytes per line, phys %pad, type %d, state %d\n",
- __func__, vou_dev->pix_idx, bytes_per_line,
- ({ dma_addr_t addr = videobuf_to_dma_contig(vb); &addr; }),
- vb->memory, vb->state);
-
+ vb2_set_plane_payload(vb, 0, size);
return 0;
}
/* Locking: caller holds fop_lock mutex and vq->irqlock spinlock */
-static void sh_vou_buf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void sh_vou_buf_queue(struct vb2_buffer *vb)
{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct sh_vou_buffer *shbuf = to_sh_vou_buffer(vb);
+ unsigned long flags;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+ spin_lock_irqsave(&vou_dev->lock, flags);
+ list_add_tail(&shbuf->list, &vou_dev->buf_list);
+ spin_unlock_irqrestore(&vou_dev->lock, flags);
+}
+
+static int sh_vou_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+ struct sh_vou_buffer *buf, *node;
+ int ret;
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &vou_dev->queue);
-
- if (vou_dev->status == SH_VOU_RUNNING) {
- return;
- } else if (!vou_dev->active) {
- vou_dev->active = vb;
- /* Start from side A: we use mirror addresses, so, set B */
- sh_vou_reg_a_write(vou_dev, VOURPR, 1);
- dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",
- __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
- sh_vou_schedule_next(vou_dev, vb);
- /* Only activate VOU after the second buffer */
- } else if (vou_dev->active->queue.next == &vb->queue) {
- /* Second buffer - initialise register side B */
- sh_vou_reg_a_write(vou_dev, VOURPR, 0);
- sh_vou_stream_start(vou_dev, vb);
-
- /* Register side switching with frame VSYNC */
- sh_vou_reg_a_write(vou_dev, VOURCR, 5);
- dev_dbg(vou_dev->v4l2_dev.dev, "%s: second buffer status 0x%x\n",
- __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
-
- /* Enable End-of-Frame (VSYNC) interrupts */
- sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
- /* Two buffers on the queue - activate the hardware */
-
- vou_dev->status = SH_VOU_RUNNING;
- sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+ vou_dev->sequence = 0;
+ ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+ video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED);
+ list_del(&buf->list);
+ }
+ vou_dev->active = NULL;
+ return ret;
}
+
+ buf = list_entry(vou_dev->buf_list.next, struct sh_vou_buffer, list);
+
+ vou_dev->active = buf;
+
+ /* Start from side A: we use mirror addresses, so, set B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 1);
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s: first buffer status 0x%x\n",
+ __func__, sh_vou_reg_a_read(vou_dev, VOUSTR));
+ sh_vou_schedule_next(vou_dev, &buf->vb);
+
+ buf = list_entry(buf->list.next, struct sh_vou_buffer, list);
+
+ /* Second buffer - initialise register side B */
+ sh_vou_reg_a_write(vou_dev, VOURPR, 0);
+ sh_vou_schedule_next(vou_dev, &buf->vb);
+
+ /* Register side switching with frame VSYNC */
+ sh_vou_reg_a_write(vou_dev, VOURCR, 5);
+
+ sh_vou_stream_config(vou_dev);
+ /* Enable End-of-Frame (VSYNC) interrupts */
+ sh_vou_reg_a_write(vou_dev, VOUIR, 0x10004);
+
+ /* Two buffers on the queue - activate the hardware */
+ vou_dev->status = SH_VOU_RUNNING;
+ sh_vou_reg_a_write(vou_dev, VOUER, 0x107);
+ return 0;
}
-static void sh_vou_buf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void sh_vou_stop_streaming(struct vb2_queue *vq)
{
- struct video_device *vdev = vq->priv_data;
- struct sh_vou_device *vou_dev = video_get_drvdata(vdev);
+ struct sh_vou_device *vou_dev = vb2_get_drv_priv(vq);
+ struct sh_vou_buffer *buf, *node;
unsigned long flags;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
+ v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
+ video, s_stream, 0);
+ /* disable output */
+ sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
+ /* ...but the current frame will complete */
+ sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ msleep(50);
spin_lock_irqsave(&vou_dev->lock, flags);
-
- if (vou_dev->active == vb) {
- /* disable output */
- sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
- /* ...but the current frame will complete */
- sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
- vou_dev->active = NULL;
- }
-
- if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED)) {
- vb->state = VIDEOBUF_ERROR;
- list_del(&vb->queue);
+ list_for_each_entry_safe(buf, node, &vou_dev->buf_list, list) {
+ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
+ list_del(&buf->list);
}
-
+ vou_dev->active = NULL;
spin_unlock_irqrestore(&vou_dev->lock, flags);
-
- free_buffer(vq, vb);
}
-static struct videobuf_queue_ops sh_vou_video_qops = {
- .buf_setup = sh_vou_buf_setup,
- .buf_prepare = sh_vou_buf_prepare,
- .buf_queue = sh_vou_buf_queue,
- .buf_release = sh_vou_buf_release,
+static struct vb2_ops sh_vou_qops = {
+ .queue_setup = sh_vou_queue_setup,
+ .buf_prepare = sh_vou_buf_prepare,
+ .buf_queue = sh_vou_buf_queue,
+ .start_streaming = sh_vou_start_streaming,
+ .stop_streaming = sh_vou_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
};
/* Video IOCTLs */
@@ -396,7 +379,10 @@ static int sh_vou_querycap(struct file *file, void *priv,
dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
strlcpy(cap->card, "SuperH VOU", sizeof(cap->card));
- cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, "sh-vou", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "platform:sh-vou", sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -540,8 +526,10 @@ static void vou_adjust_input(struct sh_vou_geometry *geo, v4l2_std_id std)
img_height_max = 576;
/* Image width must be a multiple of 4 */
- v4l_bound_align_image(&geo->in_width, 0, VOU_MAX_IMAGE_WIDTH, 2,
- &geo->in_height, 0, img_height_max, 1, 0);
+ v4l_bound_align_image(&geo->in_width,
+ VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,
+ &geo->in_height,
+ VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
/* Select scales to come as close as possible to the output image */
for (i = ARRAY_SIZE(vou_scale_h_num) - 1; i >= 0; i--) {
@@ -671,34 +659,19 @@ static void vou_adjust_output(struct sh_vou_geometry *geo, v4l2_std_id std)
vou_scale_v_num[idx_v], vou_scale_v_den[idx_v], best);
}
-static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
+static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
struct v4l2_pix_format *pix = &fmt->fmt.pix;
unsigned int img_height_max;
int pix_idx;
- struct sh_vou_geometry geo;
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- /* Revisit: is this the correct code? */
- .format.code = MEDIA_BUS_FMT_YUYV8_2X8,
- .format.field = V4L2_FIELD_INTERLACED,
- .format.colorspace = V4L2_COLORSPACE_SMPTE170M,
- };
- struct v4l2_mbus_framefmt *mbfmt = &format.format;
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
- vou_dev->rect.width, vou_dev->rect.height,
- pix->width, pix->height);
- if (pix->field == V4L2_FIELD_ANY)
- pix->field = V4L2_FIELD_NONE;
+ dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
- if (fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
- pix->field != V4L2_FIELD_NONE)
- return -EINVAL;
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ pix->ycbcr_enc = pix->quantization = 0;
for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
@@ -712,9 +685,38 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
else
img_height_max = 576;
- /* Image width must be a multiple of 4 */
- v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 2,
- &pix->height, 0, img_height_max, 1, 0);
+ v4l_bound_align_image(&pix->width,
+ VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 2,
+ &pix->height,
+ VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
+ pix->bytesperline = pix->width * vou_fmt[pix_idx].bpl;
+ pix->sizeimage = pix->height * ((pix->width * vou_fmt[pix_idx].bpp) >> 3);
+
+ return 0;
+}
+
+static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev,
+ struct v4l2_pix_format *pix)
+{
+ unsigned int img_height_max;
+ struct sh_vou_geometry geo;
+ struct v4l2_subdev_format format = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ /* Revisit: is this the correct code? */
+ .format.code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .format.field = V4L2_FIELD_INTERLACED,
+ .format.colorspace = V4L2_COLORSPACE_SMPTE170M,
+ };
+ struct v4l2_mbus_framefmt *mbfmt = &format.format;
+ int pix_idx;
+ int ret;
+
+ if (vb2_is_busy(&vou_dev->queue))
+ return -EBUSY;
+
+ for (pix_idx = 0; pix_idx < ARRAY_SIZE(vou_fmt); pix_idx++)
+ if (vou_fmt[pix_idx].pfmt == pix->pixelformat)
+ break;
geo.in_width = pix->width;
geo.in_height = pix->height;
@@ -733,6 +735,11 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u -> %ux%u\n", __func__,
geo.output.width, geo.output.height, mbfmt->width, mbfmt->height);
+ if (vou_dev->std & V4L2_STD_525_60)
+ img_height_max = 480;
+ else
+ img_height_max = 576;
+
/* Sanity checks */
if ((unsigned)mbfmt->width > VOU_MAX_IMAGE_WIDTH ||
(unsigned)mbfmt->height > img_height_max ||
@@ -765,109 +772,39 @@ static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
return 0;
}
-static int sh_vou_try_fmt_vid_out(struct file *file, void *priv,
- struct v4l2_format *fmt)
+static int sh_vou_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *fmt)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
- struct v4l2_pix_format *pix = &fmt->fmt.pix;
- int i;
+ int ret = sh_vou_try_fmt_vid_out(file, priv, fmt);
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- pix->field = V4L2_FIELD_NONE;
-
- v4l_bound_align_image(&pix->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
- &pix->height, 0, VOU_MAX_IMAGE_HEIGHT, 1, 0);
-
- for (i = 0; i < ARRAY_SIZE(vou_fmt); i++)
- if (vou_fmt[i].pfmt == pix->pixelformat)
- return 0;
-
- pix->pixelformat = vou_fmt[0].pfmt;
-
- return 0;
+ if (ret)
+ return ret;
+ return sh_vou_set_fmt_vid_out(vou_dev, &fmt->fmt.pix);
}
-static int sh_vou_reqbufs(struct file *file, void *priv,
- struct v4l2_requestbuffers *req)
+static int sh_vou_enum_output(struct file *file, void *fh,
+ struct v4l2_output *a)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
- if (req->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ if (a->index)
return -EINVAL;
-
- return videobuf_reqbufs(&vou_file->vbq, req);
-}
-
-static int sh_vou_querybuf(struct file *file, void *priv,
- struct v4l2_buffer *b)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- return videobuf_querybuf(&vou_file->vbq, b);
-}
-
-static int sh_vou_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- return videobuf_qbuf(&vou_file->vbq, b);
+ strlcpy(a->name, "Video Out", sizeof(a->name));
+ a->type = V4L2_OUTPUT_TYPE_ANALOG;
+ a->std = vou_dev->vdev.tvnorms;
+ return 0;
}
-static int sh_vou_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+static int sh_vou_g_output(struct file *file, void *fh, unsigned int *i)
{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- return videobuf_dqbuf(&vou_file->vbq, b, file->f_flags & O_NONBLOCK);
+ *i = 0;
+ return 0;
}
-static int sh_vou_streamon(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
+static int sh_vou_s_output(struct file *file, void *fh, unsigned int i)
{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0,
- video, s_stream, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* This calls our .buf_queue() (== sh_vou_buf_queue) */
- return videobuf_streamon(&vou_file->vbq);
-}
-
-static int sh_vou_streamoff(struct file *file, void *priv,
- enum v4l2_buf_type buftype)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = priv;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- /*
- * This calls buf_release from host driver's videobuf_queue_ops for all
- * remaining buffers. When the last buffer is freed, stop streaming
- */
- videobuf_streamoff(&vou_file->vbq);
- v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video, s_stream, 0);
-
- return 0;
+ return i ? -EINVAL : 0;
}
static u32 sh_vou_ntsc_mode(enum sh_vou_bus_fmt bus_fmt)
@@ -892,8 +829,11 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)
dev_dbg(vou_dev->v4l2_dev.dev, "%s(): 0x%llx\n", __func__, std_id);
- if (std_id & ~vou_dev->vdev.tvnorms)
- return -EINVAL;
+ if (std_id == vou_dev->std)
+ return 0;
+
+ if (vb2_is_busy(&vou_dev->queue))
+ return -EBUSY;
ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, video,
s_std_output, std_id);
@@ -901,13 +841,25 @@ static int sh_vou_s_std(struct file *file, void *priv, v4l2_std_id std_id)
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
- if (std_id & V4L2_STD_525_60)
+ vou_dev->rect.top = vou_dev->rect.left = 0;
+ vou_dev->rect.width = VOU_MAX_IMAGE_WIDTH;
+ if (std_id & V4L2_STD_525_60) {
sh_vou_reg_ab_set(vou_dev, VOUCR,
sh_vou_ntsc_mode(vou_dev->pdata->bus_fmt) << 29, 7 << 29);
- else
+ vou_dev->rect.height = 480;
+ } else {
sh_vou_reg_ab_set(vou_dev, VOUCR, 5 << 29, 7 << 29);
+ vou_dev->rect.height = 576;
+ }
+ vou_dev->pix.width = vou_dev->rect.width;
+ vou_dev->pix.height = vou_dev->rect.height;
+ vou_dev->pix.bytesperline =
+ vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpl;
+ vou_dev->pix.sizeimage = vou_dev->pix.height *
+ ((vou_dev->pix.width * vou_fmt[vou_dev->pix_idx].bpp) >> 3);
vou_dev->std = std_id;
+ sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);
return 0;
}
@@ -923,24 +875,66 @@ static int sh_vou_g_std(struct file *file, void *priv, v4l2_std_id *std)
return 0;
}
-static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a)
+static int sh_vou_log_status(struct file *file, void *priv)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+ pr_info("VOUER: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUER));
+ pr_info("VOUCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUCR));
+ pr_info("VOUSTR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSTR));
+ pr_info("VOUVCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVCR));
+ pr_info("VOUISR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUISR));
+ pr_info("VOUBCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUBCR));
+ pr_info("VOUDPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDPR));
+ pr_info("VOUDSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDSR));
+ pr_info("VOUVPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUVPR));
+ pr_info("VOUIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUIR));
+ pr_info("VOUSRR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSRR));
+ pr_info("VOUMSR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUMSR));
+ pr_info("VOUHIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUHIR));
+ pr_info("VOUDFR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUDFR));
+ pr_info("VOUAD1R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD1R));
+ pr_info("VOUAD2R: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAD2R));
+ pr_info("VOUAIR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUAIR));
+ pr_info("VOUSWR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOUSWR));
+ pr_info("VOURCR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURCR));
+ pr_info("VOURPR: 0x%08x\n", sh_vou_reg_a_read(vou_dev, VOURPR));
+ return 0;
+}
- a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- a->c = vou_dev->rect;
+static int sh_vou_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
+{
+ struct sh_vou_device *vou_dev = video_drvdata(file);
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+ switch (sel->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ sel->r = vou_dev->rect;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = VOU_MAX_IMAGE_WIDTH;
+ if (vou_dev->std & V4L2_STD_525_60)
+ sel->r.height = 480;
+ else
+ sel->r.height = 576;
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
/* Assume a dull encoder, do all the work ourselves. */
-static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
+static int sh_vou_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *sel)
{
- struct v4l2_crop a_writable = *a;
+ struct v4l2_rect *rect = &sel->r;
struct sh_vou_device *vou_dev = video_drvdata(file);
- struct v4l2_rect *rect = &a_writable.c;
struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT};
struct v4l2_pix_format *pix = &vou_dev->pix;
struct sh_vou_geometry geo;
@@ -954,19 +948,22 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
unsigned int img_height_max;
int ret;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s(): %ux%u@%u:%u\n", __func__,
- rect->width, rect->height, rect->left, rect->top);
-
- if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT ||
+ sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
+ if (vb2_is_busy(&vou_dev->queue))
+ return -EBUSY;
+
if (vou_dev->std & V4L2_STD_525_60)
img_height_max = 480;
else
img_height_max = 576;
- v4l_bound_align_image(&rect->width, 0, VOU_MAX_IMAGE_WIDTH, 1,
- &rect->height, 0, img_height_max, 1, 0);
+ v4l_bound_align_image(&rect->width,
+ VOU_MIN_IMAGE_WIDTH, VOU_MAX_IMAGE_WIDTH, 1,
+ &rect->height,
+ VOU_MIN_IMAGE_HEIGHT, img_height_max, 1, 0);
if (rect->width + rect->left > VOU_MAX_IMAGE_WIDTH)
rect->left = VOU_MAX_IMAGE_WIDTH - rect->width;
@@ -1021,41 +1018,11 @@ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a)
return 0;
}
-/*
- * Total field: NTSC 858 x 2 * 262/263, PAL 864 x 2 * 312/313, default rectangle
- * is the initial register values, height takes the interlaced format into
- * account. The actual image can only go up to 720 x 2 * 240, So, VOUVPR can
- * actually only meaningfully contain values <= 720 and <= 240 respectively, and
- * not <= 864 and <= 312.
- */
-static int sh_vou_cropcap(struct file *file, void *priv,
- struct v4l2_cropcap *a)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
- a->bounds.left = 0;
- a->bounds.top = 0;
- a->bounds.width = VOU_MAX_IMAGE_WIDTH;
- a->bounds.height = VOU_MAX_IMAGE_HEIGHT;
- /* Default = max, set VOUDPR = 0, which is not hardware default */
- a->defrect.left = 0;
- a->defrect.top = 0;
- a->defrect.width = VOU_MAX_IMAGE_WIDTH;
- a->defrect.height = VOU_MAX_IMAGE_HEIGHT;
- a->pixelaspect.numerator = 1;
- a->pixelaspect.denominator = 1;
-
- return 0;
-}
-
static irqreturn_t sh_vou_isr(int irq, void *dev_id)
{
struct sh_vou_device *vou_dev = dev_id;
static unsigned long j;
- struct videobuf_buffer *vb;
+ struct sh_vou_buffer *vb;
static int cnt;
u32 irq_status = sh_vou_reg_a_read(vou_dev, VOUIR), masked;
u32 vou_status = sh_vou_reg_a_read(vou_dev, VOUSTR);
@@ -1068,7 +1035,7 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
}
spin_lock(&vou_dev->lock);
- if (!vou_dev->active || list_empty(&vou_dev->queue)) {
+ if (!vou_dev->active || list_empty(&vou_dev->buf_list)) {
if (printk_timed_ratelimit(&j, 500))
dev_warn(vou_dev->v4l2_dev.dev,
"IRQ without active buffer: %x!\n", irq_status);
@@ -1090,33 +1057,30 @@ static irqreturn_t sh_vou_isr(int irq, void *dev_id)
sh_vou_reg_a_write(vou_dev, VOUIR, masked);
vb = vou_dev->active;
- list_del(&vb->queue);
-
- vb->state = VIDEOBUF_DONE;
- v4l2_get_timestamp(&vb->ts);
- vb->field_count++;
- wake_up(&vb->done);
-
- if (list_empty(&vou_dev->queue)) {
- /* Stop VOU */
- dev_dbg(vou_dev->v4l2_dev.dev, "%s: queue empty after %d\n",
- __func__, cnt);
- sh_vou_reg_a_set(vou_dev, VOUER, 0, 1);
- vou_dev->active = NULL;
- vou_dev->status = SH_VOU_INITIALISING;
- /* Disable End-of-Frame (VSYNC) interrupts */
- sh_vou_reg_a_set(vou_dev, VOUIR, 0, 0x30000);
+ if (list_is_singular(&vb->list)) {
+ /* Keep cycling while no next buffer is available */
+ sh_vou_schedule_next(vou_dev, &vb->vb);
spin_unlock(&vou_dev->lock);
return IRQ_HANDLED;
}
- vou_dev->active = list_entry(vou_dev->queue.next,
- struct videobuf_buffer, queue);
+ list_del(&vb->list);
+
+ v4l2_get_timestamp(&vb->vb.v4l2_buf.timestamp);
+ vb->vb.v4l2_buf.sequence = vou_dev->sequence++;
+ vb->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED;
+ vb2_buffer_done(&vb->vb, VB2_BUF_STATE_DONE);
- if (vou_dev->active->queue.next != &vou_dev->queue) {
- struct videobuf_buffer *new = list_entry(vou_dev->active->queue.next,
- struct videobuf_buffer, queue);
- sh_vou_schedule_next(vou_dev, new);
+ vou_dev->active = list_entry(vou_dev->buf_list.next,
+ struct sh_vou_buffer, list);
+
+ if (list_is_singular(&vou_dev->buf_list)) {
+ /* Keep cycling while no next buffer is available */
+ sh_vou_schedule_next(vou_dev, &vou_dev->active->vb);
+ } else {
+ struct sh_vou_buffer *new = list_entry(vou_dev->active->list.next,
+ struct sh_vou_buffer, list);
+ sh_vou_schedule_next(vou_dev, &new->vb);
}
spin_unlock(&vou_dev->lock);
@@ -1156,6 +1120,8 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
/* Default - fixed HSYNC length, can be made configurable is required */
sh_vou_reg_ab_write(vou_dev, VOUMSR, 0x800000);
+ sh_vou_set_fmt_vid_out(vou_dev, &vou_dev->pix);
+
return 0;
}
@@ -1163,96 +1129,47 @@ static int sh_vou_hw_init(struct sh_vou_device *vou_dev)
static int sh_vou_open(struct file *file)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = kzalloc(sizeof(struct sh_vou_file),
- GFP_KERNEL);
-
- if (!vou_file)
- return -ENOMEM;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
+ int err;
- if (mutex_lock_interruptible(&vou_dev->fop_lock)) {
- kfree(vou_file);
+ if (mutex_lock_interruptible(&vou_dev->fop_lock))
return -ERESTARTSYS;
- }
- if (atomic_inc_return(&vou_dev->use_count) == 1) {
- int ret;
+
+ err = v4l2_fh_open(file);
+ if (err)
+ goto done_open;
+ if (v4l2_fh_is_singular_file(file) &&
+ vou_dev->status == SH_VOU_INITIALISING) {
/* First open */
- vou_dev->status = SH_VOU_INITIALISING;
pm_runtime_get_sync(vou_dev->v4l2_dev.dev);
- ret = sh_vou_hw_init(vou_dev);
- if (ret < 0) {
- atomic_dec(&vou_dev->use_count);
+ err = sh_vou_hw_init(vou_dev);
+ if (err < 0) {
pm_runtime_put(vou_dev->v4l2_dev.dev);
+ v4l2_fh_release(file);
+ } else {
vou_dev->status = SH_VOU_IDLE;
- mutex_unlock(&vou_dev->fop_lock);
- kfree(vou_file);
- return ret;
}
}
-
- videobuf_queue_dma_contig_init(&vou_file->vbq, &sh_vou_video_qops,
- vou_dev->v4l2_dev.dev, &vou_dev->lock,
- V4L2_BUF_TYPE_VIDEO_OUTPUT,
- V4L2_FIELD_NONE,
- sizeof(struct videobuf_buffer),
- &vou_dev->vdev, &vou_dev->fop_lock);
+done_open:
mutex_unlock(&vou_dev->fop_lock);
-
- file->private_data = vou_file;
-
- return 0;
+ return err;
}
static int sh_vou_release(struct file *file)
{
struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = file->private_data;
+ bool is_last;
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- if (!atomic_dec_return(&vou_dev->use_count)) {
- mutex_lock(&vou_dev->fop_lock);
+ mutex_lock(&vou_dev->fop_lock);
+ is_last = v4l2_fh_is_singular_file(file);
+ _vb2_fop_release(file, NULL);
+ if (is_last) {
/* Last close */
- vou_dev->status = SH_VOU_IDLE;
+ vou_dev->status = SH_VOU_INITIALISING;
sh_vou_reg_a_set(vou_dev, VOUER, 0, 0x101);
pm_runtime_put(vou_dev->v4l2_dev.dev);
- mutex_unlock(&vou_dev->fop_lock);
}
-
- file->private_data = NULL;
- kfree(vou_file);
-
- return 0;
-}
-
-static int sh_vou_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = file->private_data;
- int ret;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- if (mutex_lock_interruptible(&vou_dev->fop_lock))
- return -ERESTARTSYS;
- ret = videobuf_mmap_mapper(&vou_file->vbq, vma);
mutex_unlock(&vou_dev->fop_lock);
- return ret;
-}
-
-static unsigned int sh_vou_poll(struct file *file, poll_table *wait)
-{
- struct sh_vou_device *vou_dev = video_drvdata(file);
- struct sh_vou_file *vou_file = file->private_data;
- unsigned int res;
-
- dev_dbg(vou_dev->v4l2_dev.dev, "%s()\n", __func__);
-
- mutex_lock(&vou_dev->fop_lock);
- res = videobuf_poll_stream(file, &vou_file->vbq, wait);
- mutex_unlock(&vou_dev->fop_lock);
- return res;
+ return 0;
}
/* sh_vou display ioctl operations */
@@ -1262,17 +1179,23 @@ static const struct v4l2_ioctl_ops sh_vou_ioctl_ops = {
.vidioc_g_fmt_vid_out = sh_vou_g_fmt_vid_out,
.vidioc_s_fmt_vid_out = sh_vou_s_fmt_vid_out,
.vidioc_try_fmt_vid_out = sh_vou_try_fmt_vid_out,
- .vidioc_reqbufs = sh_vou_reqbufs,
- .vidioc_querybuf = sh_vou_querybuf,
- .vidioc_qbuf = sh_vou_qbuf,
- .vidioc_dqbuf = sh_vou_dqbuf,
- .vidioc_streamon = sh_vou_streamon,
- .vidioc_streamoff = sh_vou_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_g_output = sh_vou_g_output,
+ .vidioc_s_output = sh_vou_s_output,
+ .vidioc_enum_output = sh_vou_enum_output,
.vidioc_s_std = sh_vou_s_std,
.vidioc_g_std = sh_vou_g_std,
- .vidioc_cropcap = sh_vou_cropcap,
- .vidioc_g_crop = sh_vou_g_crop,
- .vidioc_s_crop = sh_vou_s_crop,
+ .vidioc_g_selection = sh_vou_g_selection,
+ .vidioc_s_selection = sh_vou_s_selection,
+ .vidioc_log_status = sh_vou_log_status,
};
static const struct v4l2_file_operations sh_vou_fops = {
@@ -1280,8 +1203,9 @@ static const struct v4l2_file_operations sh_vou_fops = {
.open = sh_vou_open,
.release = sh_vou_release,
.unlocked_ioctl = video_ioctl2,
- .mmap = sh_vou_mmap,
- .poll = sh_vou_poll,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+ .write = vb2_fop_write,
};
static const struct video_device sh_vou_video_template = {
@@ -1300,8 +1224,9 @@ static int sh_vou_probe(struct platform_device *pdev)
struct i2c_adapter *i2c_adap;
struct video_device *vdev;
struct sh_vou_device *vou_dev;
- struct resource *reg_res, *region;
+ struct resource *reg_res;
struct v4l2_subdev *subdev;
+ struct vb2_queue *q;
int irq, ret;
reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1312,16 +1237,16 @@ static int sh_vou_probe(struct platform_device *pdev)
return -ENODEV;
}
- vou_dev = kzalloc(sizeof(*vou_dev), GFP_KERNEL);
+ vou_dev = devm_kzalloc(&pdev->dev, sizeof(*vou_dev), GFP_KERNEL);
if (!vou_dev)
return -ENOMEM;
- INIT_LIST_HEAD(&vou_dev->queue);
+ INIT_LIST_HEAD(&vou_dev->buf_list);
spin_lock_init(&vou_dev->lock);
mutex_init(&vou_dev->fop_lock);
- atomic_set(&vou_dev->use_count, 0);
vou_dev->pdata = vou_pdata;
- vou_dev->status = SH_VOU_IDLE;
+ vou_dev->status = SH_VOU_INITIALISING;
+ vou_dev->pix_idx = 1;
rect = &vou_dev->rect;
pix = &vou_dev->pix;
@@ -1334,34 +1259,24 @@ static int sh_vou_probe(struct platform_device *pdev)
rect->height = 480;
pix->width = VOU_MAX_IMAGE_WIDTH;
pix->height = 480;
- pix->pixelformat = V4L2_PIX_FMT_YVYU;
- pix->field = V4L2_FIELD_NONE;
- pix->bytesperline = VOU_MAX_IMAGE_WIDTH * 2;
+ pix->pixelformat = V4L2_PIX_FMT_NV16;
+ pix->field = V4L2_FIELD_INTERLACED;
+ pix->bytesperline = VOU_MAX_IMAGE_WIDTH;
pix->sizeimage = VOU_MAX_IMAGE_WIDTH * 2 * 480;
pix->colorspace = V4L2_COLORSPACE_SMPTE170M;
- region = request_mem_region(reg_res->start, resource_size(reg_res),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "VOU region already claimed\n");
- ret = -EBUSY;
- goto ereqmemreg;
- }
+ vou_dev->base = devm_ioremap_resource(&pdev->dev, reg_res);
+ if (IS_ERR(vou_dev->base))
+ return PTR_ERR(vou_dev->base);
- vou_dev->base = ioremap(reg_res->start, resource_size(reg_res));
- if (!vou_dev->base) {
- ret = -ENOMEM;
- goto emap;
- }
-
- ret = request_irq(irq, sh_vou_isr, 0, "vou", vou_dev);
+ ret = devm_request_irq(&pdev->dev, irq, sh_vou_isr, 0, "vou", vou_dev);
if (ret < 0)
- goto ereqirq;
+ return ret;
ret = v4l2_device_register(&pdev->dev, &vou_dev->v4l2_dev);
if (ret < 0) {
dev_err(&pdev->dev, "Error registering v4l2 device\n");
- goto ev4l2devreg;
+ return ret;
}
vdev = &vou_dev->vdev;
@@ -1374,6 +1289,30 @@ static int sh_vou_probe(struct platform_device *pdev)
video_set_drvdata(vdev, vou_dev);
+ /* Initialize the vb2 queue */
+ q = &vou_dev->queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_WRITE;
+ q->drv_priv = vou_dev;
+ q->buf_struct_size = sizeof(struct sh_vou_buffer);
+ q->ops = &sh_vou_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->min_buffers_needed = 2;
+ q->lock = &vou_dev->fop_lock;
+ ret = vb2_queue_init(q);
+ if (ret)
+ goto einitctx;
+
+ vou_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(vou_dev->alloc_ctx)) {
+ dev_err(&pdev->dev, "Can't allocate buffer context");
+ ret = PTR_ERR(vou_dev->alloc_ctx);
+ goto einitctx;
+ }
+ vdev->queue = q;
+ INIT_LIST_HEAD(&vou_dev->buf_list);
+
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
@@ -1405,41 +1344,27 @@ ei2cnd:
ereset:
i2c_put_adapter(i2c_adap);
ei2cgadap:
+ vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
+einitctx:
pm_runtime_disable(&pdev->dev);
v4l2_device_unregister(&vou_dev->v4l2_dev);
-ev4l2devreg:
- free_irq(irq, vou_dev);
-ereqirq:
- iounmap(vou_dev->base);
-emap:
- release_mem_region(reg_res->start, resource_size(reg_res));
-ereqmemreg:
- kfree(vou_dev);
return ret;
}
static int sh_vou_remove(struct platform_device *pdev)
{
- int irq = platform_get_irq(pdev, 0);
struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
struct sh_vou_device *vou_dev = container_of(v4l2_dev,
struct sh_vou_device, v4l2_dev);
struct v4l2_subdev *sd = list_entry(v4l2_dev->subdevs.next,
struct v4l2_subdev, list);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- struct resource *reg_res;
- if (irq > 0)
- free_irq(irq, vou_dev);
pm_runtime_disable(&pdev->dev);
video_unregister_device(&vou_dev->vdev);
i2c_put_adapter(client->adapter);
+ vb2_dma_contig_cleanup_ctx(vou_dev->alloc_ctx);
v4l2_device_unregister(&vou_dev->v4l2_dev);
- iounmap(vou_dev->base);
- reg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (reg_res)
- release_mem_region(reg_res->start, resource_size(reg_res));
- kfree(vou_dev);
return 0;
}
diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c
index 287902681..90701726a 100644
--- a/drivers/media/platform/soc_camera/atmel-isi.c
+++ b/drivers/media/platform/soc_camera/atmel-isi.c
@@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <media/atmel-isi.h>
@@ -34,7 +35,6 @@
#define VID_LIMIT_BYTES (16 * 1024 * 1024)
#define MIN_FRAME_RATE 15
#define FRAME_INTERVAL_MILLI_SEC (1000 / MIN_FRAME_RATE)
-#define ISI_DEFAULT_MCLK_FREQ 25000000
/* Frame buffer descriptor */
struct fbd {
@@ -82,8 +82,6 @@ struct atmel_isi {
struct completion complete;
/* ISI peripherial clock */
struct clk *pclk;
- /* ISI_MCK, feed to camera sensor to generate pixel clock */
- struct clk *mck;
unsigned int irq;
struct isi_platform_data pdata;
@@ -386,10 +384,13 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct atmel_isi *isi = ici->priv;
int ret;
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
/* Reset ISI */
ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET);
if (ret < 0) {
dev_err(icd->parent, "Reset ISI timed out\n");
+ pm_runtime_put(ici->v4l2_dev.dev);
return ret;
}
/* Disable all interrupts */
@@ -431,11 +432,9 @@ static void stop_streaming(struct vb2_queue *vq)
time_before(jiffies, timeout))
msleep(1);
- if (time_after(jiffies, timeout)) {
+ if (time_after(jiffies, timeout))
dev_err(icd->parent,
"Timeout waiting for finishing codec request\n");
- return;
- }
/* Disable interrupts */
isi_writel(isi, ISI_INTDIS,
@@ -445,6 +444,8 @@ static void stop_streaming(struct vb2_queue *vq)
ret = atmel_isi_wait_status(isi, WAIT_ISI_DISABLE);
if (ret < 0)
dev_err(icd->parent, "Disable ISI timed out\n");
+
+ pm_runtime_put(ici->v4l2_dev.dev);
}
static struct vb2_ops isi_video_qops = {
@@ -516,7 +517,13 @@ static int isi_camera_set_fmt(struct soc_camera_device *icd,
if (mf->code != xlate->code)
return -EINVAL;
+ /* Enable PM and peripheral clock before operate isi registers */
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
ret = configure_geometry(isi, pix->width, pix->height, xlate->code);
+
+ pm_runtime_put(ici->v4l2_dev.dev);
+
if (ret < 0)
return ret;
@@ -730,37 +737,6 @@ static void isi_camera_remove_device(struct soc_camera_device *icd)
icd->devnum);
}
-/* Called with .host_lock held */
-static int isi_camera_clock_start(struct soc_camera_host *ici)
-{
- struct atmel_isi *isi = ici->priv;
- int ret;
-
- ret = clk_prepare_enable(isi->pclk);
- if (ret)
- return ret;
-
- if (!IS_ERR(isi->mck)) {
- ret = clk_prepare_enable(isi->mck);
- if (ret) {
- clk_disable_unprepare(isi->pclk);
- return ret;
- }
- }
-
- return 0;
-}
-
-/* Called with .host_lock held */
-static void isi_camera_clock_stop(struct soc_camera_host *ici)
-{
- struct atmel_isi *isi = ici->priv;
-
- if (!IS_ERR(isi->mck))
- clk_disable_unprepare(isi->mck);
- clk_disable_unprepare(isi->pclk);
-}
-
static unsigned int isi_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
@@ -855,9 +831,14 @@ static int isi_camera_set_bus_param(struct soc_camera_device *icd)
cfg1 |= ISI_CFG1_THMASK_BEATS_16;
+ /* Enable PM and peripheral clock before operate isi registers */
+ pm_runtime_get_sync(ici->v4l2_dev.dev);
+
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
isi_writel(isi, ISI_CFG1, cfg1);
+ pm_runtime_put(ici->v4l2_dev.dev);
+
return 0;
}
@@ -865,8 +846,6 @@ static struct soc_camera_host_ops isi_soc_camera_host_ops = {
.owner = THIS_MODULE,
.add = isi_camera_add_device,
.remove = isi_camera_remove_device,
- .clock_start = isi_camera_clock_start,
- .clock_stop = isi_camera_clock_stop,
.set_fmt = isi_camera_set_fmt,
.try_fmt = isi_camera_try_fmt,
.get_formats = isi_camera_get_formats,
@@ -889,6 +868,7 @@ static int atmel_isi_remove(struct platform_device *pdev)
sizeof(struct fbd) * MAX_BUFFER_NUM,
isi->p_fb_descriptors,
isi->fb_descriptors_phys);
+ pm_runtime_disable(&pdev->dev);
return 0;
}
@@ -902,7 +882,6 @@ static int atmel_isi_probe_dt(struct atmel_isi *isi,
/* Default settings for ISI */
isi->pdata.full_mode = 1;
- isi->pdata.mck_hz = ISI_DEFAULT_MCLK_FREQ;
isi->pdata.frate = ISI_CFG1_FRATE_CAPTURE_ALL;
np = of_graph_get_next_endpoint(np, NULL);
@@ -978,21 +957,6 @@ static int atmel_isi_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&isi->video_buffer_list);
INIT_LIST_HEAD(&isi->dma_desc_head);
- /* ISI_MCK is the sensor master clock. It should be handled by the
- * sensor driver directly, as the ISI has no use for that clock. Make
- * the clock optional here while platforms transition to the correct
- * model.
- */
- isi->mck = devm_clk_get(dev, "isi_mck");
- if (!IS_ERR(isi->mck)) {
- /* Set ISI_MCK's frequency, it should be faster than pixel
- * clock.
- */
- ret = clk_set_rate(isi->mck, isi->pdata.mck_hz);
- if (ret < 0)
- return ret;
- }
-
isi->p_fb_descriptors = dma_alloc_coherent(&pdev->dev,
sizeof(struct fbd) * MAX_BUFFER_NUM,
&isi->fb_descriptors_phys,
@@ -1027,8 +991,6 @@ static int atmel_isi_probe(struct platform_device *pdev)
if (isi->pdata.data_width_flags & ISI_DATAWIDTH_10)
isi->width_flags |= 1 << 9;
- isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
-
irq = platform_get_irq(pdev, 0);
if (IS_ERR_VALUE(irq)) {
ret = irq;
@@ -1049,6 +1011,9 @@ static int atmel_isi_probe(struct platform_device *pdev)
soc_host->v4l2_dev.dev = &pdev->dev;
soc_host->nr = pdev->id;
+ pm_suspend_ignore_children(&pdev->dev, true);
+ pm_runtime_enable(&pdev->dev);
+
if (isi->pdata.asd_sizes) {
soc_host->asd = isi->pdata.asd;
soc_host->asd_sizes = isi->pdata.asd_sizes;
@@ -1062,6 +1027,7 @@ static int atmel_isi_probe(struct platform_device *pdev)
return 0;
err_register_soc_camera_host:
+ pm_runtime_disable(&pdev->dev);
err_req_irq:
err_ioremap:
vb2_dma_contig_cleanup_ctx(isi->alloc_ctx);
@@ -1074,6 +1040,30 @@ err_alloc_ctx:
return ret;
}
+static int atmel_isi_runtime_suspend(struct device *dev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(dev);
+ struct atmel_isi *isi = container_of(soc_host,
+ struct atmel_isi, soc_host);
+
+ clk_disable_unprepare(isi->pclk);
+
+ return 0;
+}
+static int atmel_isi_runtime_resume(struct device *dev)
+{
+ struct soc_camera_host *soc_host = to_soc_camera_host(dev);
+ struct atmel_isi *isi = container_of(soc_host,
+ struct atmel_isi, soc_host);
+
+ return clk_prepare_enable(isi->pclk);
+}
+
+static const struct dev_pm_ops atmel_isi_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(atmel_isi_runtime_suspend,
+ atmel_isi_runtime_resume, NULL)
+};
+
static const struct of_device_id atmel_isi_of_match[] = {
{ .compatible = "atmel,at91sam9g45-isi" },
{ }
@@ -1085,6 +1075,7 @@ static struct platform_driver atmel_isi_driver = {
.driver = {
.name = "atmel_isi",
.of_match_table = of_match_ptr(atmel_isi_of_match),
+ .pm = &atmel_isi_dev_pm_ops,
},
};
diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c
index db7700b0a..71dd71c0b 100644
--- a/drivers/media/platform/soc_camera/rcar_vin.c
+++ b/drivers/media/platform/soc_camera/rcar_vin.c
@@ -98,6 +98,7 @@
#define VNMC_INF_YUV10_BT656 (2 << 16)
#define VNMC_INF_YUV10_BT601 (3 << 16)
#define VNMC_INF_YUV16 (5 << 16)
+#define VNMC_INF_RGB888 (6 << 16)
#define VNMC_VUP (1 << 10)
#define VNMC_IM_ODD (0 << 3)
#define VNMC_IM_ODD_EVEN (1 << 3)
@@ -540,6 +541,9 @@ static int rcar_vin_videobuf_setup(struct vb2_queue *vq,
unsigned int bytes_per_line;
int ret;
+ if (fmt->fmt.pix.sizeimage < icd->sizeimage)
+ return -EINVAL;
+
xlate = soc_camera_xlate_by_fourcc(icd,
fmt->fmt.pix.pixelformat);
if (!xlate)
@@ -589,7 +593,7 @@ 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;
+ bool progressive = false, output_is_yuv = false, input_is_yuv = false;
switch (priv->field) {
case V4L2_FIELD_TOP:
@@ -623,16 +627,22 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
case MEDIA_BUS_FMT_YUYV8_1X16:
/* BT.601/BT.1358 16bit YCbCr422 */
vnmc |= VNMC_INF_YUV16;
+ input_is_yuv = true;
break;
case MEDIA_BUS_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;
+ input_is_yuv = true;
+ break;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ vnmc |= VNMC_INF_RGB888;
break;
case MEDIA_BUS_FMT_YUYV10_2X10:
/* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */
vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ?
VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601;
+ input_is_yuv = true;
break;
default:
break;
@@ -676,7 +686,7 @@ static int rcar_vin_setup(struct rcar_vin_priv *priv)
vnmc |= VNMC_VUP;
/* If input and output use the same colorspace, use bypass mode */
- if (output_is_yuv)
+ if (input_is_yuv == output_is_yuv)
vnmc |= VNMC_BPS;
/* progressive or interlaced mode */
@@ -1423,6 +1433,7 @@ static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx,
case MEDIA_BUS_FMT_YUYV8_1X16:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_RGB888_1X24:
if (cam->extra_fmt)
break;
@@ -1783,6 +1794,7 @@ static int rcar_vin_querycap(struct soc_camera_host *ici,
strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card));
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr);
return 0;
}
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 c5c6c4e91..efdeea449 100644
--- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
+++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
@@ -1665,6 +1665,8 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici,
struct v4l2_capability *cap)
{
strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card));
+ strlcpy(cap->driver, "sh_mobile_ceu", sizeof(cap->driver));
+ strlcpy(cap->bus_info, "platform:sh_mobile_ceu", sizeof(cap->bus_info));
cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -1773,6 +1775,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev)
pcdev->max_height = pcdev->pdata->max_height;
pcdev->flags = pcdev->pdata->flags;
}
+ pcdev->field = V4L2_FIELD_NONE;
if (!pcdev->max_width) {
unsigned int v;
diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c
index d708df410..9087fed58 100644
--- a/drivers/media/platform/soc_camera/soc_camera.c
+++ b/drivers/media/platform/soc_camera/soc_camera.c
@@ -309,11 +309,14 @@ static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
static int soc_camera_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
+ struct soc_camera_device *icd = file->private_data;
+
if (inp->index != 0)
return -EINVAL;
/* default is camera */
inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = icd->vdev->tvnorms;
strcpy(inp->name, "Camera");
return 0;
@@ -381,9 +384,8 @@ static int soc_camera_reqbufs(struct file *file, void *priv,
ret = vb2_reqbufs(&icd->vb2_vidq, p);
}
- if (!ret && !icd->streamer)
- icd->streamer = file;
-
+ if (!ret)
+ icd->streamer = p->count ? file : NULL;
return ret;
}
@@ -440,12 +442,19 @@ static int soc_camera_create_bufs(struct file *file, void *priv,
{
struct soc_camera_device *icd = file->private_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
/* videobuf2 only */
if (ici->ops->init_videobuf)
- return -EINVAL;
- else
- return vb2_create_bufs(&icd->vb2_vidq, create);
+ return -ENOTTY;
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+
+ ret = vb2_create_bufs(&icd->vb2_vidq, create);
+ if (!ret)
+ icd->streamer = file;
+ return ret;
}
static int soc_camera_prepare_buf(struct file *file, void *priv,
@@ -467,14 +476,13 @@ static int soc_camera_expbuf(struct file *file, void *priv,
struct soc_camera_device *icd = file->private_data;
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
- if (icd->streamer != file)
- return -EBUSY;
-
/* videobuf2 only */
if (ici->ops->init_videobuf)
- return -EINVAL;
- else
- return vb2_expbuf(&icd->vb2_vidq, p);
+ return -ENOTTY;
+
+ if (icd->streamer && icd->streamer != file)
+ return -EBUSY;
+ return vb2_expbuf(&icd->vb2_vidq, p);
}
/* Always entered with .host_lock held */
@@ -780,20 +788,21 @@ static int soc_camera_close(struct file *file)
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
mutex_lock(&ici->host_lock);
+ if (icd->streamer == file) {
+ if (ici->ops->init_videobuf2)
+ vb2_queue_release(&icd->vb2_vidq);
+ icd->streamer = NULL;
+ }
icd->use_count--;
if (!icd->use_count) {
pm_runtime_suspend(&icd->vdev->dev);
pm_runtime_disable(&icd->vdev->dev);
- if (ici->ops->init_videobuf2)
- vb2_queue_release(&icd->vb2_vidq);
__soc_camera_power_off(icd);
soc_camera_remove_device(icd);
}
- if (icd->streamer == file)
- icd->streamer = NULL;
mutex_unlock(&ici->host_lock);
module_put(ici->ops->owner);
@@ -992,6 +1001,7 @@ static int soc_camera_streamoff(struct file *file, void *priv,
struct soc_camera_device *icd = file->private_data;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ int ret;
WARN_ON(priv != file->private_data);
@@ -1006,13 +1016,13 @@ static int soc_camera_streamoff(struct file *file, void *priv,
* remaining buffers. When the last buffer is freed, stop capture
*/
if (ici->ops->init_videobuf)
- videobuf_streamoff(&icd->vb_vidq);
+ ret = videobuf_streamoff(&icd->vb_vidq);
else
- vb2_streamoff(&icd->vb2_vidq, i);
+ ret = vb2_streamoff(&icd->vb2_vidq, i);
v4l2_subdev_call(sd, video, s_stream, 0);
- return 0;
+ return ret;
}
static int soc_camera_cropcap(struct file *file, void *fh,
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c
index 18282a0f8..79c56356a 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c
@@ -116,6 +116,9 @@ static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val)
case BDISP_RGB565:
seq_puts(s, "RGB565 - ");
break;
+ case BDISP_RGB888:
+ seq_puts(s, "RGB888 - ");
+ break;
case BDISP_XRGB8888:
seq_puts(s, "xRGB888 - ");
break;
@@ -185,6 +188,9 @@ static void bdisp_dbg_dump_sty(struct seq_file *s,
case BDISP_RGB565:
seq_puts(s, "RGB565 - ");
break;
+ case BDISP_RGB888:
+ seq_puts(s, "RGB888 - ");
+ break;
case BDISP_XRGB8888:
seq_puts(s, "xRGB888 - ");
break;
@@ -420,6 +426,8 @@ static const char *bdisp_fmt_to_str(struct bdisp_frame frame)
return "NV12";
case V4L2_PIX_FMT_RGB565:
return "RGB16";
+ case V4L2_PIX_FMT_RGB24:
+ return "RGB24";
case V4L2_PIX_FMT_XBGR32:
return "XRGB";
case V4L2_PIX_FMT_ABGR32:
diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c
index 465828e85..052c932ac 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-hw.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c
@@ -336,8 +336,8 @@ static int bdisp_hw_get_hv_inc(struct bdisp_ctx *ctx, u16 *h_inc, u16 *v_inc)
src_w = ctx->src.crop.width;
src_h = ctx->src.crop.height;
- dst_w = ctx->dst.width;
- dst_h = ctx->dst.height;
+ dst_w = ctx->dst.crop.width;
+ dst_h = ctx->dst.crop.height;
if (bdisp_hw_get_inc(src_w, dst_w, h_inc) ||
bdisp_hw_get_inc(src_h, dst_h, v_inc)) {
@@ -483,9 +483,9 @@ static void bdisp_hw_build_node(struct bdisp_ctx *ctx,
src_rect.width -= src_x_offset;
src_rect.width = min_t(__s32, MAX_SRC_WIDTH, src_rect.width);
- dst_x_offset = (src_x_offset * dst->width) / ctx->src.crop.width;
+ dst_x_offset = (src_x_offset * dst_width) / ctx->src.crop.width;
dst_rect.left += dst_x_offset;
- dst_rect.width = (src_rect.width * dst->width) / ctx->src.crop.width;
+ dst_rect.width = (src_rect.width * dst_width) / ctx->src.crop.width;
/* General */
src_fmt = src->fmt->pixelformat;
@@ -768,12 +768,12 @@ static void bdisp_hw_save_request(struct bdisp_ctx *ctx)
/* Allocate memory if not done yet */
if (!copy_node[i]) {
copy_node[i] = devm_kzalloc(ctx->bdisp_dev->dev,
- sizeof(*copy_node),
+ sizeof(*copy_node[i]),
GFP_KERNEL);
if (!copy_node[i])
return;
}
- copy_node[i] = node[i];
+ *copy_node[i] = *node[i];
}
}
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
index 9e782ebe1..df61355b4 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
@@ -851,33 +851,56 @@ static int bdisp_g_selection(struct file *file, void *fh,
struct bdisp_frame *frame;
struct bdisp_ctx *ctx = fh_to_ctx(fh);
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- /* Composing / capture is not supported */
- dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n");
- return -EINVAL;
- }
-
frame = ctx_get_frame(ctx, s->type);
if (IS_ERR(frame)) {
dev_err(ctx->bdisp_dev->dev, "Invalid frame (%p)\n", frame);
return PTR_ERR(frame);
}
- switch (s->target) {
- case V4L2_SEL_TGT_CROP:
- /* cropped frame */
- s->r = frame->crop;
+ switch (s->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ /* cropped frame */
+ s->r = frame->crop;
+ break;
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ /* complete frame */
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = frame->width;
+ s->r.height = frame->height;
+ break;
+ default:
+ dev_err(ctx->bdisp_dev->dev, "Invalid target\n");
+ return -EINVAL;
+ }
break;
- case V4L2_SEL_TGT_CROP_DEFAULT:
- case V4L2_SEL_TGT_CROP_BOUNDS:
- /* complete frame */
- s->r.left = 0;
- s->r.top = 0;
- s->r.width = frame->width;
- s->r.height = frame->height;
+
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ /* composed (cropped) frame */
+ s->r = frame->crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ /* complete frame */
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = frame->width;
+ s->r.height = frame->height;
+ break;
+ default:
+ dev_err(ctx->bdisp_dev->dev, "Invalid target\n");
+ return -EINVAL;
+ }
break;
+
default:
- dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n");
+ dev_err(ctx->bdisp_dev->dev, "Invalid type\n");
return -EINVAL;
}
@@ -906,15 +929,18 @@ static int bdisp_s_selection(struct file *file, void *fh,
struct bdisp_frame *frame;
struct bdisp_ctx *ctx = fh_to_ctx(fh);
struct v4l2_rect *in, out;
+ bool valid = false;
- if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) {
- /* Composing / capture is not supported */
- dev_dbg(ctx->bdisp_dev->dev, "Not supported for capture\n");
- return -EINVAL;
- }
+ if ((s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) &&
+ (s->target == V4L2_SEL_TGT_CROP))
+ valid = true;
+
+ if ((s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
+ (s->target == V4L2_SEL_TGT_COMPOSE))
+ valid = true;
- if (s->target != V4L2_SEL_TGT_CROP) {
- dev_dbg(ctx->bdisp_dev->dev, "Invalid target\n");
+ if (!valid) {
+ dev_err(ctx->bdisp_dev->dev, "Invalid type / target\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/sti/c8sectpfe/Kconfig
new file mode 100644
index 000000000..641ad8f34
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/Kconfig
@@ -0,0 +1,28 @@
+config DVB_C8SECTPFE
+ tristate "STMicroelectronics C8SECTPFE DVB support"
+ depends on PINCTRL && DVB_CORE && I2C
+ depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
+ select FW_LOADER
+ select FW_LOADER_USER_HELPER_FALLBACK
+ select DEBUG_FS
+ select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV6110 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0900 if MEDIA_SUBDRV_AUTOSELECT
+ select DVB_STV0367 if MEDIA_SUBDRV_AUTOSELECT
+ select MEDIA_TUNER_TDA18212 if MEDIA_SUBDRV_AUTOSELECT
+
+ ---help---
+ This adds support for DVB front-end cards connected
+ to TS inputs of STiH407/410 SoC.
+
+ The driver currently supports C8SECTPFE's TS input block,
+ memdma engine, and HW PID filtering.
+
+ Supported DVB front-end cards are:
+ - STMicroelectronics DVB-T B2100A (STV0367 + TDA18212)
+ - STMicroelectronics DVB-S/S2 STV0903 + STV6110 + LNBP24 board
+
+ To compile this driver as a module, choose M here: the
+ module will be called c8sectpfe.
diff --git a/drivers/media/platform/sti/c8sectpfe/Makefile b/drivers/media/platform/sti/c8sectpfe/Makefile
new file mode 100644
index 000000000..b578c7cb4
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/Makefile
@@ -0,0 +1,9 @@
+c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \
+ c8sectpfe-debugfs.o
+
+obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
+
+ccflags-y += -Idrivers/media/i2c
+ccflags-y += -Idrivers/media/common
+ccflags-y += -Idrivers/media/dvb-core/ -Idrivers/media/dvb-frontends/ \
+ -Idrivers/media/tuners/
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
new file mode 100644
index 000000000..95223ab71
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
@@ -0,0 +1,265 @@
+/*
+ * c8sectpfe-common.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dvb/dmx.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+
+#include "dmxdev.h"
+#include "dvbdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-dvb.h"
+
+static int register_dvb(struct stdemux *demux, struct dvb_adapter *adap,
+ void *start_feed, void *stop_feed,
+ struct c8sectpfei *fei)
+{
+ int result;
+
+ demux->dvb_demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+
+ demux->dvb_demux.priv = demux;
+ demux->dvb_demux.filternum = C8SECTPFE_MAXCHANNEL;
+ demux->dvb_demux.feednum = C8SECTPFE_MAXCHANNEL;
+
+ demux->dvb_demux.start_feed = start_feed;
+ demux->dvb_demux.stop_feed = stop_feed;
+ demux->dvb_demux.write_to_decoder = NULL;
+
+ result = dvb_dmx_init(&demux->dvb_demux);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_dmx_init failed (errno = %d)\n",
+ result);
+ goto err_dmx;
+ }
+
+ demux->dmxdev.filternum = demux->dvb_demux.filternum;
+ demux->dmxdev.demux = &demux->dvb_demux.dmx;
+ demux->dmxdev.capabilities = 0;
+
+ result = dvb_dmxdev_init(&demux->dmxdev, adap);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_dmxdev_init failed (errno = %d)\n",
+ result);
+
+ goto err_dmxdev;
+ }
+
+ demux->hw_frontend.source = DMX_FRONTEND_0 + demux->tsin_index;
+
+ result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "add_frontend failed (errno = %d)\n", result);
+ goto err_fe_hw;
+ }
+
+ demux->mem_frontend.source = DMX_MEMORY_FE;
+ result = demux->dvb_demux.dmx.add_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "add_frontend failed (%d)\n", result);
+ goto err_fe_mem;
+ }
+
+ result = demux->dvb_demux.dmx.connect_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+ if (result < 0) {
+ dev_err(fei->dev, "connect_frontend (%d)\n", result);
+ goto err_fe_con;
+ }
+
+ return 0;
+
+err_fe_con:
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+err_fe_mem:
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+err_fe_hw:
+ dvb_dmxdev_release(&demux->dmxdev);
+err_dmxdev:
+ dvb_dmx_release(&demux->dvb_demux);
+err_dmx:
+ return result;
+
+}
+
+static void unregister_dvb(struct stdemux *demux)
+{
+
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->mem_frontend);
+
+ demux->dvb_demux.dmx.remove_frontend(&demux->dvb_demux.dmx,
+ &demux->hw_frontend);
+
+ dvb_dmxdev_release(&demux->dmxdev);
+
+ dvb_dmx_release(&demux->dvb_demux);
+}
+
+static struct c8sectpfe *c8sectpfe_create(struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed)
+{
+ struct c8sectpfe *c8sectpfe;
+ int result;
+ int i, j;
+
+ short int ids[] = { -1 };
+
+ c8sectpfe = kzalloc(sizeof(struct c8sectpfe), GFP_KERNEL);
+ if (!c8sectpfe)
+ goto err1;
+
+ mutex_init(&c8sectpfe->lock);
+
+ c8sectpfe->device = fei->dev;
+
+ result = dvb_register_adapter(&c8sectpfe->adapter, "STi c8sectpfe",
+ THIS_MODULE, fei->dev, ids);
+ if (result < 0) {
+ dev_err(fei->dev, "dvb_register_adapter failed (errno = %d)\n",
+ result);
+ goto err2;
+ }
+
+ c8sectpfe->adapter.priv = fei;
+
+ for (i = 0; i < fei->tsin_count; i++) {
+
+ c8sectpfe->demux[i].tsin_index = i;
+ c8sectpfe->demux[i].c8sectpfei = fei;
+
+ result = register_dvb(&c8sectpfe->demux[i], &c8sectpfe->adapter,
+ start_feed, stop_feed, fei);
+ if (result < 0) {
+ dev_err(fei->dev,
+ "register_dvb feed=%d failed (errno = %d)\n",
+ result, i);
+
+ /* we take a all or nothing approach */
+ for (j = 0; j < i; j++)
+ unregister_dvb(&c8sectpfe->demux[j]);
+ goto err3;
+ }
+ }
+
+ c8sectpfe->num_feeds = fei->tsin_count;
+
+ return c8sectpfe;
+err3:
+ dvb_unregister_adapter(&c8sectpfe->adapter);
+err2:
+ kfree(c8sectpfe);
+err1:
+ return NULL;
+};
+
+static void c8sectpfe_delete(struct c8sectpfe *c8sectpfe)
+{
+ int i;
+
+ if (!c8sectpfe)
+ return;
+
+ for (i = 0; i < c8sectpfe->num_feeds; i++)
+ unregister_dvb(&c8sectpfe->demux[i]);
+
+ dvb_unregister_adapter(&c8sectpfe->adapter);
+
+ kfree(c8sectpfe);
+};
+
+void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
+ struct c8sectpfei *fei)
+{
+ int n;
+ struct channel_info *tsin;
+
+ for (n = 0; n < fei->tsin_count; n++) {
+
+ tsin = fei->channel_data[n];
+
+ if (tsin && tsin->frontend) {
+ dvb_unregister_frontend(tsin->frontend);
+ dvb_frontend_detach(tsin->frontend);
+ }
+
+ if (tsin && tsin->i2c_adapter)
+ i2c_put_adapter(tsin->i2c_adapter);
+
+ if (tsin && tsin->i2c_client) {
+ if (tsin->i2c_client->dev.driver->owner)
+ module_put(tsin->i2c_client->dev.driver->owner);
+ i2c_unregister_device(tsin->i2c_client);
+ }
+ }
+
+ c8sectpfe_delete(c8sectpfe);
+};
+
+int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
+ struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed)
+{
+ struct channel_info *tsin;
+ struct dvb_frontend *frontend;
+ int n, res;
+
+ *c8sectpfe = c8sectpfe_create(fei, start_feed, stop_feed);
+ if (!*c8sectpfe)
+ return -ENOMEM;
+
+ for (n = 0; n < fei->tsin_count; n++) {
+ tsin = fei->channel_data[n];
+
+ res = c8sectpfe_frontend_attach(&frontend, *c8sectpfe, tsin, n);
+ if (res)
+ goto err;
+
+ res = dvb_register_frontend(&c8sectpfe[0]->adapter, frontend);
+ if (res < 0) {
+ dev_err(fei->dev, "dvb_register_frontend failed (%d)\n",
+ res);
+ goto err;
+ }
+
+ tsin->frontend = frontend;
+ }
+
+ return 0;
+
+err:
+ c8sectpfe_tuner_unregister_frontend(*c8sectpfe, fei);
+ return res;
+}
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h
new file mode 100644
index 000000000..da21c0ac0
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h
@@ -0,0 +1,64 @@
+/*
+ * c8sectpfe-common.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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 _C8SECTPFE_COMMON_H_
+#define _C8SECTPFE_COMMON_H_
+
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/gpio.h>
+#include <linux/version.h>
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+/* Maximum number of channels */
+#define C8SECTPFE_MAXADAPTER (4)
+#define C8SECTPFE_MAXCHANNEL 64
+#define STPTI_MAXCHANNEL 64
+
+#define MAX_INPUTBLOCKS 7
+
+struct c8sectpfe;
+struct stdemux;
+
+struct stdemux {
+ struct dvb_demux dvb_demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend hw_frontend;
+ struct dmx_frontend mem_frontend;
+ int tsin_index;
+ int running_feed_count;
+ struct c8sectpfei *c8sectpfei;
+};
+
+struct c8sectpfe {
+ struct stdemux demux[MAX_INPUTBLOCKS];
+ struct mutex lock;
+ struct dvb_adapter adapter;
+ struct device *device;
+ int mapping;
+ int num_feeds;
+};
+
+/* Channel registration */
+int c8sectpfe_tuner_register_frontend(struct c8sectpfe **c8sectpfe,
+ struct c8sectpfei *fei,
+ void *start_feed,
+ void *stop_feed);
+
+void c8sectpfe_tuner_unregister_frontend(struct c8sectpfe *c8sectpfe,
+ struct c8sectpfei *fei);
+
+#endif
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
new file mode 100644
index 000000000..ab969b7a4
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
@@ -0,0 +1,1235 @@
+/*
+ * c8sectpfe-core.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author:Peter Bennett <peter.bennett@st.com>
+ * Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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/atomic.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dvb/dmx.h>
+#include <linux/dvb/frontend.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-debugfs.h"
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_frontend.h"
+#include "dvb_net.h"
+
+#define FIRMWARE_MEMDMA "/*(DEBLOBBED)*/"
+/*(DEBLOBBED)*/
+
+#define PID_TABLE_SIZE 1024
+#define POLL_MSECS 50
+
+static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei);
+
+#define TS_PKT_SIZE 188
+#define HEADER_SIZE (4)
+#define PACKET_SIZE (TS_PKT_SIZE+HEADER_SIZE)
+
+#define FEI_ALIGNMENT (32)
+/* hw requires minimum of 8*PACKET_SIZE and padded to 8byte boundary */
+#define FEI_BUFFER_SIZE (8*PACKET_SIZE*340)
+
+#define FIFO_LEN 1024
+
+static void c8sectpfe_timer_interrupt(unsigned long ac8sectpfei)
+{
+ struct c8sectpfei *fei = (struct c8sectpfei *)ac8sectpfei;
+ struct channel_info *channel;
+ int chan_num;
+
+ /* iterate through input block channels */
+ for (chan_num = 0; chan_num < fei->tsin_count; chan_num++) {
+ channel = fei->channel_data[chan_num];
+
+ /* is this descriptor initialised and TP enabled */
+ if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE))
+ tasklet_schedule(&channel->tsklet);
+ }
+
+ fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS);
+ add_timer(&fei->timer);
+}
+
+static void channel_swdemux_tsklet(unsigned long data)
+{
+ struct channel_info *channel = (struct channel_info *)data;
+ struct c8sectpfei *fei = channel->fei;
+ unsigned long wp, rp;
+ int pos, num_packets, n, size;
+ u8 *buf;
+
+ if (unlikely(!channel || !channel->irec))
+ return;
+
+ wp = readl(channel->irec + DMA_PRDS_BUSWP_TP(0));
+ rp = readl(channel->irec + DMA_PRDS_BUSRP_TP(0));
+
+ pos = rp - channel->back_buffer_busaddr;
+
+ /* has it wrapped */
+ if (wp < rp)
+ wp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE;
+
+ size = wp - rp;
+ num_packets = size / PACKET_SIZE;
+
+ /* manage cache so data is visible to CPU */
+ dma_sync_single_for_cpu(fei->dev,
+ rp,
+ size,
+ DMA_FROM_DEVICE);
+
+ buf = (u8 *) channel->back_buffer_aligned;
+
+ dev_dbg(fei->dev,
+ "chan=%d channel=%p num_packets = %d, buf = %p, pos = 0x%x\n\t"
+ "rp=0x%lx, wp=0x%lx\n",
+ channel->tsin_id, channel, num_packets, buf, pos, rp, wp);
+
+ for (n = 0; n < num_packets; n++) {
+ dvb_dmx_swfilter_packets(
+ &fei->c8sectpfe[0]->
+ demux[channel->demux_mapping].dvb_demux,
+ &buf[pos], 1);
+
+ pos += PACKET_SIZE;
+ }
+
+ /* advance the read pointer */
+ if (wp == (channel->back_buffer_busaddr + FEI_BUFFER_SIZE))
+ writel(channel->back_buffer_busaddr, channel->irec +
+ DMA_PRDS_BUSRP_TP(0));
+ else
+ writel(wp, channel->irec + DMA_PRDS_BUSWP_TP(0));
+}
+
+static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct stdemux *stdemux = (struct stdemux *)demux->priv;
+ struct c8sectpfei *fei = stdemux->c8sectpfei;
+ struct channel_info *channel;
+ u32 tmp;
+ unsigned long *bitmap;
+
+ switch (dvbdmxfeed->type) {
+ case DMX_TYPE_TS:
+ break;
+ case DMX_TYPE_SEC:
+ break;
+ default:
+ dev_err(fei->dev, "%s:%d Error bailing\n"
+ , __func__, __LINE__);
+ return -EINVAL;
+ }
+
+ if (dvbdmxfeed->type == DMX_TYPE_TS) {
+ switch (dvbdmxfeed->pes_type) {
+ case DMX_PES_VIDEO:
+ case DMX_PES_AUDIO:
+ case DMX_PES_TELETEXT:
+ case DMX_PES_PCR:
+ case DMX_PES_OTHER:
+ break;
+ default:
+ dev_err(fei->dev, "%s:%d Error bailing\n"
+ , __func__, __LINE__);
+ return -EINVAL;
+ }
+ }
+
+ if (!atomic_read(&fei->fw_loaded)) {
+ dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&fei->lock);
+
+ channel = fei->channel_data[stdemux->tsin_index];
+
+ bitmap = (unsigned long *) channel->pid_buffer_aligned;
+
+ /* 8192 is a special PID */
+ if (dvbdmxfeed->pid == 8192) {
+ tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
+ tmp &= ~C8SECTPFE_PID_ENABLE;
+ writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
+
+ } else {
+ bitmap_set(bitmap, dvbdmxfeed->pid, 1);
+ }
+
+ /* manage cache so PID bitmap is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ channel->active = 1;
+
+ if (fei->global_feed_count == 0) {
+ fei->timer.expires = jiffies +
+ msecs_to_jiffies(msecs_to_jiffies(POLL_MSECS));
+
+ add_timer(&fei->timer);
+ }
+
+ if (stdemux->running_feed_count == 0) {
+
+ dev_dbg(fei->dev, "Starting channel=%p\n", channel);
+
+ tasklet_init(&channel->tsklet, channel_swdemux_tsklet,
+ (unsigned long) channel);
+
+ /* Reset the internal inputblock sram pointers */
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_BUFF_STRT(channel->tsin_id));
+ writel(channel->fifo + FIFO_LEN - 1,
+ fei->io + C8SECTPFE_IB_BUFF_END(channel->tsin_id));
+
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_READ_PNT(channel->tsin_id));
+ writel(channel->fifo,
+ fei->io + C8SECTPFE_IB_WRT_PNT(channel->tsin_id));
+
+
+ /* reset read / write memdma ptrs for this channel */
+ writel(channel->back_buffer_busaddr, channel->irec +
+ DMA_PRDS_BUSBASE_TP(0));
+
+ tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0));
+
+ writel(channel->back_buffer_busaddr, channel->irec +
+ DMA_PRDS_BUSWP_TP(0));
+
+ /* Issue a reset and enable InputBlock */
+ writel(C8SECTPFE_SYS_ENABLE | C8SECTPFE_SYS_RESET
+ , fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
+
+ /* and enable the tp */
+ writel(0x1, channel->irec + DMA_PRDS_TPENABLE);
+
+ dev_dbg(fei->dev, "%s:%d Starting DMA feed on stdemux=%p\n"
+ , __func__, __LINE__, stdemux);
+ }
+
+ stdemux->running_feed_count++;
+ fei->global_feed_count++;
+
+ mutex_unlock(&fei->lock);
+
+ return 0;
+}
+
+static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ struct dvb_demux *demux = dvbdmxfeed->demux;
+ struct stdemux *stdemux = (struct stdemux *)demux->priv;
+ struct c8sectpfei *fei = stdemux->c8sectpfei;
+ struct channel_info *channel;
+ int idlereq;
+ u32 tmp;
+ int ret;
+ unsigned long *bitmap;
+
+ if (!atomic_read(&fei->fw_loaded)) {
+ dev_err(fei->dev, "%s: c8sectpfe fw not loaded\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&fei->lock);
+
+ channel = fei->channel_data[stdemux->tsin_index];
+
+ bitmap = (unsigned long *) channel->pid_buffer_aligned;
+
+ if (dvbdmxfeed->pid == 8192) {
+ tmp = readl(fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
+ tmp |= C8SECTPFE_PID_ENABLE;
+ writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(channel->tsin_id));
+ } else {
+ bitmap_clear(bitmap, dvbdmxfeed->pid, 1);
+ }
+
+ /* manage cache so data is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ if (--stdemux->running_feed_count == 0) {
+
+ channel = fei->channel_data[stdemux->tsin_index];
+
+ /* TP re-configuration on page 168 of functional spec */
+
+ /* disable IB (prevents more TS data going to memdma) */
+ writel(0, fei->io + C8SECTPFE_IB_SYS(channel->tsin_id));
+
+ /* disable this channels descriptor */
+ writel(0, channel->irec + DMA_PRDS_TPENABLE);
+
+ tasklet_disable(&channel->tsklet);
+
+ /* now request memdma channel goes idle */
+ idlereq = (1 << channel->tsin_id) | IDLEREQ;
+ writel(idlereq, fei->io + DMA_IDLE_REQ);
+
+ /* wait for idle irq handler to signal completion */
+ ret = wait_for_completion_timeout(&channel->idle_completion,
+ msecs_to_jiffies(100));
+
+ if (ret == 0)
+ dev_warn(fei->dev,
+ "Timeout waiting for idle irq on tsin%d\n",
+ channel->tsin_id);
+
+ reinit_completion(&channel->idle_completion);
+
+ /* reset read / write ptrs for this channel */
+
+ writel(channel->back_buffer_busaddr,
+ channel->irec + DMA_PRDS_BUSBASE_TP(0));
+
+ tmp = channel->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, channel->irec + DMA_PRDS_BUSTOP_TP(0));
+
+ writel(channel->back_buffer_busaddr,
+ channel->irec + DMA_PRDS_BUSWP_TP(0));
+
+ dev_dbg(fei->dev,
+ "%s:%d stopping DMA feed on stdemux=%p channel=%d\n",
+ __func__, __LINE__, stdemux, channel->tsin_id);
+
+ /* turn off all PIDS in the bitmap */
+ memset((void *)channel->pid_buffer_aligned
+ , 0x00, PID_TABLE_SIZE);
+
+ /* manage cache so data is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ channel->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ channel->active = 0;
+ }
+
+ if (--fei->global_feed_count == 0) {
+ dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
+ , __func__, __LINE__, fei->global_feed_count);
+
+ del_timer(&fei->timer);
+ }
+
+ mutex_unlock(&fei->lock);
+
+ return 0;
+}
+
+static struct channel_info *find_channel(struct c8sectpfei *fei, int tsin_num)
+{
+ int i;
+
+ for (i = 0; i < C8SECTPFE_MAX_TSIN_CHAN; i++) {
+ if (!fei->channel_data[i])
+ continue;
+
+ if (fei->channel_data[i]->tsin_id == tsin_num)
+ return fei->channel_data[i];
+ }
+
+ return NULL;
+}
+
+static void c8sectpfe_getconfig(struct c8sectpfei *fei)
+{
+ struct c8sectpfe_hw *hw = &fei->hw_stats;
+
+ hw->num_ib = readl(fei->io + SYS_CFG_NUM_IB);
+ hw->num_mib = readl(fei->io + SYS_CFG_NUM_MIB);
+ hw->num_swts = readl(fei->io + SYS_CFG_NUM_SWTS);
+ hw->num_tsout = readl(fei->io + SYS_CFG_NUM_TSOUT);
+ hw->num_ccsc = readl(fei->io + SYS_CFG_NUM_CCSC);
+ hw->num_ram = readl(fei->io + SYS_CFG_NUM_RAM);
+ hw->num_tp = readl(fei->io + SYS_CFG_NUM_TP);
+
+ dev_info(fei->dev, "C8SECTPFE hw supports the following:\n");
+ dev_info(fei->dev, "Input Blocks: %d\n", hw->num_ib);
+ dev_info(fei->dev, "Merged Input Blocks: %d\n", hw->num_mib);
+ dev_info(fei->dev, "Software Transport Stream Inputs: %d\n"
+ , hw->num_swts);
+ dev_info(fei->dev, "Transport Stream Output: %d\n", hw->num_tsout);
+ dev_info(fei->dev, "Cable Card Converter: %d\n", hw->num_ccsc);
+ dev_info(fei->dev, "RAMs supported by C8SECTPFE: %d\n", hw->num_ram);
+ dev_info(fei->dev, "Tango TPs supported by C8SECTPFE: %d\n"
+ , hw->num_tp);
+}
+
+static irqreturn_t c8sectpfe_idle_irq_handler(int irq, void *priv)
+{
+ struct c8sectpfei *fei = priv;
+ struct channel_info *chan;
+ int bit;
+ unsigned long tmp = readl(fei->io + DMA_IDLE_REQ);
+
+ /* page 168 of functional spec: Clear the idle request
+ by writing 0 to the C8SECTPFE_DMA_IDLE_REQ register. */
+
+ /* signal idle completion */
+ for_each_set_bit(bit, &tmp, fei->hw_stats.num_ib) {
+
+ chan = find_channel(fei, bit);
+
+ if (chan)
+ complete(&chan->idle_completion);
+ }
+
+ writel(0, fei->io + DMA_IDLE_REQ);
+
+ return IRQ_HANDLED;
+}
+
+
+static void free_input_block(struct c8sectpfei *fei, struct channel_info *tsin)
+{
+ if (!fei || !tsin)
+ return;
+
+ if (tsin->back_buffer_busaddr)
+ if (!dma_mapping_error(fei->dev, tsin->back_buffer_busaddr))
+ dma_unmap_single(fei->dev, tsin->back_buffer_busaddr,
+ FEI_BUFFER_SIZE, DMA_BIDIRECTIONAL);
+
+ kfree(tsin->back_buffer_start);
+
+ if (tsin->pid_buffer_busaddr)
+ if (!dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr))
+ dma_unmap_single(fei->dev, tsin->pid_buffer_busaddr,
+ PID_TABLE_SIZE, DMA_BIDIRECTIONAL);
+
+ kfree(tsin->pid_buffer_start);
+}
+
+#define MAX_NAME 20
+
+static int configure_memdma_and_inputblock(struct c8sectpfei *fei,
+ struct channel_info *tsin)
+{
+ int ret;
+ u32 tmp;
+ char tsin_pin_name[MAX_NAME];
+
+ if (!fei || !tsin)
+ return -EINVAL;
+
+ dev_dbg(fei->dev, "%s:%d Configuring channel=%p tsin=%d\n"
+ , __func__, __LINE__, tsin, tsin->tsin_id);
+
+ init_completion(&tsin->idle_completion);
+
+ tsin->back_buffer_start = kzalloc(FEI_BUFFER_SIZE +
+ FEI_ALIGNMENT, GFP_KERNEL);
+
+ if (!tsin->back_buffer_start) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ /* Ensure backbuffer is 32byte aligned */
+ tsin->back_buffer_aligned = tsin->back_buffer_start
+ + FEI_ALIGNMENT;
+
+ tsin->back_buffer_aligned = (void *)
+ (((uintptr_t) tsin->back_buffer_aligned) & ~0x1F);
+
+ tsin->back_buffer_busaddr = dma_map_single(fei->dev,
+ (void *)tsin->back_buffer_aligned,
+ FEI_BUFFER_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(fei->dev, tsin->back_buffer_busaddr)) {
+ dev_err(fei->dev, "failed to map back_buffer\n");
+ ret = -EFAULT;
+ goto err_unmap;
+ }
+
+ /*
+ * The pid buffer can be configured (in hw) for byte or bit
+ * per pid. By powers of deduction we conclude stih407 family
+ * is configured (at SoC design stage) for bit per pid.
+ */
+ tsin->pid_buffer_start = kzalloc(2048, GFP_KERNEL);
+
+ if (!tsin->pid_buffer_start) {
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ /*
+ * PID buffer needs to be aligned to size of the pid table
+ * which at bit per pid is 1024 bytes (8192 pids / 8).
+ * PIDF_BASE register enforces this alignment when writing
+ * the register.
+ */
+
+ tsin->pid_buffer_aligned = tsin->pid_buffer_start +
+ PID_TABLE_SIZE;
+
+ tsin->pid_buffer_aligned = (void *)
+ (((uintptr_t) tsin->pid_buffer_aligned) & ~0x3ff);
+
+ tsin->pid_buffer_busaddr = dma_map_single(fei->dev,
+ tsin->pid_buffer_aligned,
+ PID_TABLE_SIZE,
+ DMA_BIDIRECTIONAL);
+
+ if (dma_mapping_error(fei->dev, tsin->pid_buffer_busaddr)) {
+ dev_err(fei->dev, "failed to map pid_bitmap\n");
+ ret = -EFAULT;
+ goto err_unmap;
+ }
+
+ /* manage cache so pid bitmap is visible to HW */
+ dma_sync_single_for_device(fei->dev,
+ tsin->pid_buffer_busaddr,
+ PID_TABLE_SIZE,
+ DMA_TO_DEVICE);
+
+ snprintf(tsin_pin_name, MAX_NAME, "tsin%d-%s", tsin->tsin_id,
+ (tsin->serial_not_parallel ? "serial" : "parallel"));
+
+ tsin->pstate = pinctrl_lookup_state(fei->pinctrl, tsin_pin_name);
+ if (IS_ERR(tsin->pstate)) {
+ dev_err(fei->dev, "%s: pinctrl_lookup_state couldn't find %s state\n"
+ , __func__, tsin_pin_name);
+ ret = PTR_ERR(tsin->pstate);
+ goto err_unmap;
+ }
+
+ ret = pinctrl_select_state(fei->pinctrl, tsin->pstate);
+
+ if (ret) {
+ dev_err(fei->dev, "%s: pinctrl_select_state failed\n"
+ , __func__);
+ goto err_unmap;
+ }
+
+ /* Enable this input block */
+ tmp = readl(fei->io + SYS_INPUT_CLKEN);
+ tmp |= BIT(tsin->tsin_id);
+ writel(tmp, fei->io + SYS_INPUT_CLKEN);
+
+ if (tsin->serial_not_parallel)
+ tmp |= C8SECTPFE_SERIAL_NOT_PARALLEL;
+
+ if (tsin->invert_ts_clk)
+ tmp |= C8SECTPFE_INVERT_TSCLK;
+
+ if (tsin->async_not_sync)
+ tmp |= C8SECTPFE_ASYNC_NOT_SYNC;
+
+ tmp |= C8SECTPFE_ALIGN_BYTE_SOP | C8SECTPFE_BYTE_ENDIANNESS_MSB;
+
+ writel(tmp, fei->io + C8SECTPFE_IB_IP_FMT_CFG(tsin->tsin_id));
+
+ writel(C8SECTPFE_SYNC(0x9) |
+ C8SECTPFE_DROP(0x9) |
+ C8SECTPFE_TOKEN(0x47),
+ fei->io + C8SECTPFE_IB_SYNCLCKDRP_CFG(tsin->tsin_id));
+
+ writel(TS_PKT_SIZE, fei->io + C8SECTPFE_IB_PKT_LEN(tsin->tsin_id));
+
+ /* Place the FIFO's at the end of the irec descriptors */
+
+ tsin->fifo = (tsin->tsin_id * FIFO_LEN);
+
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id));
+ writel(tsin->fifo + FIFO_LEN - 1,
+ fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id));
+
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id));
+ writel(tsin->fifo, fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id));
+
+ writel(tsin->pid_buffer_busaddr,
+ fei->io + PIDF_BASE(tsin->tsin_id));
+
+ dev_info(fei->dev, "chan=%d PIDF_BASE=0x%x pid_bus_addr=%pad\n",
+ tsin->tsin_id, readl(fei->io + PIDF_BASE(tsin->tsin_id)),
+ &tsin->pid_buffer_busaddr);
+
+ /* Configure and enable HW PID filtering */
+
+ /*
+ * The PID value is created by assembling the first 8 bytes of
+ * the TS packet into a 64-bit word in big-endian format. A
+ * slice of that 64-bit word is taken from
+ * (PID_OFFSET+PID_NUM_BITS-1) to PID_OFFSET.
+ */
+ tmp = (C8SECTPFE_PID_ENABLE | C8SECTPFE_PID_NUMBITS(13)
+ | C8SECTPFE_PID_OFFSET(40));
+
+ writel(tmp, fei->io + C8SECTPFE_IB_PID_SET(tsin->tsin_id));
+
+ dev_dbg(fei->dev, "chan=%d setting wp: %d, rp: %d, buf: %d-%d\n",
+ tsin->tsin_id,
+ readl(fei->io + C8SECTPFE_IB_WRT_PNT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_READ_PNT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_BUFF_STRT(tsin->tsin_id)),
+ readl(fei->io + C8SECTPFE_IB_BUFF_END(tsin->tsin_id)));
+
+ /* Get base addpress of pointer record block from DMEM */
+ tsin->irec = fei->io + DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET +
+ readl(fei->io + DMA_PTRREC_BASE);
+
+ /* fill out pointer record data structure */
+
+ /* advance pointer record block to our channel */
+ tsin->irec += (tsin->tsin_id * DMA_PRDS_SIZE);
+
+ writel(tsin->fifo, tsin->irec + DMA_PRDS_MEMBASE);
+
+ writel(tsin->fifo + FIFO_LEN - 1, tsin->irec + DMA_PRDS_MEMTOP);
+
+ writel((188 + 7)&~7, tsin->irec + DMA_PRDS_PKTSIZE);
+
+ writel(0x1, tsin->irec + DMA_PRDS_TPENABLE);
+
+ /* read/write pointers with physical bus address */
+
+ writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSBASE_TP(0));
+
+ tmp = tsin->back_buffer_busaddr + FEI_BUFFER_SIZE - 1;
+ writel(tmp, tsin->irec + DMA_PRDS_BUSTOP_TP(0));
+
+ writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0));
+ writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0));
+
+ /* initialize tasklet */
+ tasklet_init(&tsin->tsklet, channel_swdemux_tsklet,
+ (unsigned long) tsin);
+
+ return 0;
+
+err_unmap:
+ free_input_block(fei, tsin);
+ return ret;
+}
+
+static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
+{
+ struct c8sectpfei *fei = priv;
+
+ dev_err(fei->dev, "%s: error handling not yet implemented\n"
+ , __func__);
+
+ /*
+ * TODO FIXME we should detect some error conditions here
+ * and ideally so something about them!
+ */
+
+ return IRQ_HANDLED;
+}
+
+static int c8sectpfe_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *child, *np = dev->of_node;
+ struct c8sectpfei *fei;
+ struct resource *res;
+ int ret, index = 0;
+ struct channel_info *tsin;
+
+ /* Allocate the c8sectpfei structure */
+ fei = devm_kzalloc(dev, sizeof(struct c8sectpfei), GFP_KERNEL);
+ if (!fei)
+ return -ENOMEM;
+
+ fei->dev = dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "c8sectpfe");
+ fei->io = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fei->io))
+ return PTR_ERR(fei->io);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "c8sectpfe-ram");
+ fei->sram = devm_ioremap_resource(dev, res);
+ if (IS_ERR(fei->sram))
+ return PTR_ERR(fei->sram);
+
+ fei->sram_size = res->end - res->start;
+
+ fei->idle_irq = platform_get_irq_byname(pdev, "c8sectpfe-idle-irq");
+ if (fei->idle_irq < 0) {
+ dev_err(dev, "Can't get c8sectpfe-idle-irq\n");
+ return fei->idle_irq;
+ }
+
+ fei->error_irq = platform_get_irq_byname(pdev, "c8sectpfe-error-irq");
+ if (fei->error_irq < 0) {
+ dev_err(dev, "Can't get c8sectpfe-error-irq\n");
+ return fei->error_irq;
+ }
+
+ platform_set_drvdata(pdev, fei);
+
+ fei->c8sectpfeclk = devm_clk_get(dev, "c8sectpfe");
+ if (IS_ERR(fei->c8sectpfeclk)) {
+ dev_err(dev, "c8sectpfe clk not found\n");
+ return PTR_ERR(fei->c8sectpfeclk);
+ }
+
+ ret = clk_prepare_enable(fei->c8sectpfeclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable c8sectpfe clock\n");
+ return ret;
+ }
+
+ /* to save power disable all IP's (on by default) */
+ writel(0, fei->io + SYS_INPUT_CLKEN);
+
+ /* Enable memdma clock */
+ writel(MEMDMAENABLE, fei->io + SYS_OTHER_CLKEN);
+
+ /* clear internal sram */
+ memset_io(fei->sram, 0x0, fei->sram_size);
+
+ c8sectpfe_getconfig(fei);
+
+ ret = devm_request_irq(dev, fei->idle_irq, c8sectpfe_idle_irq_handler,
+ 0, "c8sectpfe-idle-irq", fei);
+ if (ret) {
+ dev_err(dev, "Can't register c8sectpfe-idle-irq IRQ.\n");
+ goto err_clk_disable;
+ }
+
+ ret = devm_request_irq(dev, fei->error_irq,
+ c8sectpfe_error_irq_handler, 0,
+ "c8sectpfe-error-irq", fei);
+ if (ret) {
+ dev_err(dev, "Can't register c8sectpfe-error-irq IRQ.\n");
+ goto err_clk_disable;
+ }
+
+ fei->tsin_count = of_get_child_count(np);
+
+ if (fei->tsin_count > C8SECTPFE_MAX_TSIN_CHAN ||
+ fei->tsin_count > fei->hw_stats.num_ib) {
+
+ dev_err(dev, "More tsin declared than exist on SoC!\n");
+ ret = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ fei->pinctrl = devm_pinctrl_get(dev);
+
+ if (IS_ERR(fei->pinctrl)) {
+ dev_err(dev, "Error getting tsin pins\n");
+ ret = PTR_ERR(fei->pinctrl);
+ goto err_clk_disable;
+ }
+
+ for_each_child_of_node(np, child) {
+ struct device_node *i2c_bus;
+
+ fei->channel_data[index] = devm_kzalloc(dev,
+ sizeof(struct channel_info),
+ GFP_KERNEL);
+
+ if (!fei->channel_data[index]) {
+ ret = -ENOMEM;
+ goto err_clk_disable;
+ }
+
+ tsin = fei->channel_data[index];
+
+ tsin->fei = fei;
+
+ ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
+ if (ret) {
+ dev_err(&pdev->dev, "No tsin_num found\n");
+ goto err_clk_disable;
+ }
+
+ /* sanity check value */
+ if (tsin->tsin_id > fei->hw_stats.num_ib) {
+ dev_err(&pdev->dev,
+ "tsin-num %d specified greater than number\n\t"
+ "of input block hw in SoC! (%d)",
+ tsin->tsin_id, fei->hw_stats.num_ib);
+ ret = -EINVAL;
+ goto err_clk_disable;
+ }
+
+ tsin->invert_ts_clk = of_property_read_bool(child,
+ "invert-ts-clk");
+
+ tsin->serial_not_parallel = of_property_read_bool(child,
+ "serial-not-parallel");
+
+ tsin->async_not_sync = of_property_read_bool(child,
+ "async-not-sync");
+
+ ret = of_property_read_u32(child, "dvb-card",
+ &tsin->dvb_card);
+ if (ret) {
+ dev_err(&pdev->dev, "No dvb-card found\n");
+ goto err_clk_disable;
+ }
+
+ i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
+ if (!i2c_bus) {
+ dev_err(&pdev->dev, "No i2c-bus found\n");
+ goto err_clk_disable;
+ }
+ tsin->i2c_adapter =
+ of_find_i2c_adapter_by_node(i2c_bus);
+ if (!tsin->i2c_adapter) {
+ dev_err(&pdev->dev, "No i2c adapter found\n");
+ of_node_put(i2c_bus);
+ goto err_clk_disable;
+ }
+ of_node_put(i2c_bus);
+
+ tsin->rst_gpio = of_get_named_gpio(child, "rst-gpio", 0);
+
+ ret = gpio_is_valid(tsin->rst_gpio);
+ if (!ret) {
+ dev_err(dev,
+ "reset gpio for tsin%d not valid (gpio=%d)\n",
+ tsin->tsin_id, tsin->rst_gpio);
+ goto err_clk_disable;
+ }
+
+ ret = devm_gpio_request_one(dev, tsin->rst_gpio,
+ GPIOF_OUT_INIT_LOW, "NIM reset");
+ if (ret && ret != -EBUSY) {
+ dev_err(dev, "Can't request tsin%d reset gpio\n"
+ , fei->channel_data[index]->tsin_id);
+ goto err_clk_disable;
+ }
+
+ if (!ret) {
+ /* toggle reset lines */
+ gpio_direction_output(tsin->rst_gpio, 0);
+ usleep_range(3500, 5000);
+ gpio_direction_output(tsin->rst_gpio, 1);
+ usleep_range(3000, 5000);
+ }
+
+ tsin->demux_mapping = index;
+
+ dev_dbg(fei->dev,
+ "channel=%p n=%d tsin_num=%d, invert-ts-clk=%d\n\t"
+ "serial-not-parallel=%d pkt-clk-valid=%d dvb-card=%d\n",
+ fei->channel_data[index], index,
+ tsin->tsin_id, tsin->invert_ts_clk,
+ tsin->serial_not_parallel, tsin->async_not_sync,
+ tsin->dvb_card);
+
+ index++;
+ }
+
+ /* Setup timer interrupt */
+ init_timer(&fei->timer);
+ fei->timer.function = c8sectpfe_timer_interrupt;
+ fei->timer.data = (unsigned long)fei;
+
+ mutex_init(&fei->lock);
+
+ /* Get the configuration information about the tuners */
+ ret = c8sectpfe_tuner_register_frontend(&fei->c8sectpfe[0],
+ (void *)fei,
+ c8sectpfe_start_feed,
+ c8sectpfe_stop_feed);
+ if (ret) {
+ dev_err(dev, "c8sectpfe_tuner_register_frontend failed (%d)\n",
+ ret);
+ goto err_clk_disable;
+ }
+
+ /* ensure all other init has been done before requesting firmware */
+ ret = load_c8sectpfe_fw_step1(fei);
+ if (ret) {
+ dev_err(dev, "Couldn't load slim core firmware\n");
+ goto err_clk_disable;
+ }
+
+ c8sectpfe_debugfs_init(fei);
+
+ return 0;
+
+err_clk_disable:
+ /* TODO uncomment when upstream has taken a reference on this clk */
+ /*clk_disable_unprepare(fei->c8sectpfeclk);*/
+ return ret;
+}
+
+static int c8sectpfe_remove(struct platform_device *pdev)
+{
+ struct c8sectpfei *fei = platform_get_drvdata(pdev);
+ struct channel_info *channel;
+ int i;
+
+ wait_for_completion(&fei->fw_ack);
+
+ c8sectpfe_tuner_unregister_frontend(fei->c8sectpfe[0], fei);
+
+ /*
+ * Now loop through and un-configure each of the InputBlock resources
+ */
+ for (i = 0; i < fei->tsin_count; i++) {
+ channel = fei->channel_data[i];
+ free_input_block(fei, channel);
+ }
+
+ c8sectpfe_debugfs_exit(fei);
+
+ dev_info(fei->dev, "Stopping memdma SLIM core\n");
+ if (readl(fei->io + DMA_CPU_RUN))
+ writel(0x0, fei->io + DMA_CPU_RUN);
+
+ /* unclock all internal IP's */
+ if (readl(fei->io + SYS_INPUT_CLKEN))
+ writel(0, fei->io + SYS_INPUT_CLKEN);
+
+ if (readl(fei->io + SYS_OTHER_CLKEN))
+ writel(0, fei->io + SYS_OTHER_CLKEN);
+
+ /* TODO uncomment when upstream has taken a reference on this clk */
+ /*
+ if (fei->c8sectpfeclk)
+ clk_disable_unprepare(fei->c8sectpfeclk);
+ */
+
+ return 0;
+}
+
+
+static int configure_channels(struct c8sectpfei *fei)
+{
+ int index = 0, ret;
+ struct channel_info *tsin;
+ struct device_node *child, *np = fei->dev->of_node;
+
+ /* iterate round each tsin and configure memdma descriptor and IB hw */
+ for_each_child_of_node(np, child) {
+
+ tsin = fei->channel_data[index];
+
+ ret = configure_memdma_and_inputblock(fei,
+ fei->channel_data[index]);
+
+ if (ret) {
+ dev_err(fei->dev,
+ "configure_memdma_and_inputblock failed\n");
+ goto err_unmap;
+ }
+ index++;
+ }
+
+ return 0;
+
+err_unmap:
+ for (index = 0; index < fei->tsin_count; index++) {
+ tsin = fei->channel_data[index];
+ free_input_block(fei, tsin);
+ }
+ return ret;
+}
+
+static int
+c8sectpfe_elf_sanity_check(struct c8sectpfei *fei, const struct firmware *fw)
+{
+ struct elf32_hdr *ehdr;
+ char class;
+
+ if (!fw) {
+ dev_err(fei->dev, "failed to load %s\n", FIRMWARE_MEMDMA);
+ return -EINVAL;
+ }
+
+ if (fw->size < sizeof(struct elf32_hdr)) {
+ dev_err(fei->dev, "Image is too small\n");
+ return -EINVAL;
+ }
+
+ ehdr = (struct elf32_hdr *)fw->data;
+
+ /* We only support ELF32 at this point */
+ class = ehdr->e_ident[EI_CLASS];
+ if (class != ELFCLASS32) {
+ dev_err(fei->dev, "Unsupported class: %d\n", class);
+ return -EINVAL;
+ }
+
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+ dev_err(fei->dev, "Unsupported firmware endianness\n");
+ return -EINVAL;
+ }
+
+ if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
+ dev_err(fei->dev, "Image is too small\n");
+ return -EINVAL;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+ dev_err(fei->dev, "Image is corrupted (bad magic)\n");
+ return -EINVAL;
+ }
+
+ /* Check ELF magic */
+ ehdr = (Elf32_Ehdr *)fw->data;
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ ehdr->e_ident[EI_MAG3] != ELFMAG3) {
+ dev_err(fei->dev, "Invalid ELF magic\n");
+ return -EINVAL;
+ }
+
+ if (ehdr->e_type != ET_EXEC) {
+ dev_err(fei->dev, "Unsupported ELF header type\n");
+ return -EINVAL;
+ }
+
+ if (ehdr->e_phoff > fw->size) {
+ dev_err(fei->dev, "Firmware size is too small\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static void load_imem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr,
+ const struct firmware *fw, u8 __iomem *dest,
+ int seg_num)
+{
+ const u8 *imem_src = fw->data + phdr->p_offset;
+ int i;
+
+ /*
+ * For IMEM segments, the segment contains 24-bit
+ * instructions which must be padded to 32-bit
+ * instructions before being written. The written
+ * segment is padded with NOP instructions.
+ */
+
+ dev_dbg(fei->dev,
+ "Loading IMEM segment %d 0x%08x\n\t"
+ " (0x%x bytes) -> 0x%p (0x%x bytes)\n", seg_num,
+ phdr->p_paddr, phdr->p_filesz,
+ dest, phdr->p_memsz + phdr->p_memsz / 3);
+
+ for (i = 0; i < phdr->p_filesz; i++) {
+
+ writeb(readb((void __iomem *)imem_src), (void __iomem *)dest);
+
+ /* Every 3 bytes, add an additional
+ * padding zero in destination */
+ if (i % 3 == 2) {
+ dest++;
+ writeb(0x00, (void __iomem *)dest);
+ }
+
+ dest++;
+ imem_src++;
+ }
+}
+
+static void load_dmem_segment(struct c8sectpfei *fei, Elf32_Phdr *phdr,
+ const struct firmware *fw, u8 __iomem *dst, int seg_num)
+{
+ /*
+ * For DMEM segments copy the segment data from the ELF
+ * file and pad segment with zeroes
+ */
+
+ dev_dbg(fei->dev,
+ "Loading DMEM segment %d 0x%08x\n\t"
+ "(0x%x bytes) -> 0x%p (0x%x bytes)\n",
+ seg_num, phdr->p_paddr, phdr->p_filesz,
+ dst, phdr->p_memsz);
+
+ memcpy((void __iomem *)dst, (void *)fw->data + phdr->p_offset,
+ phdr->p_filesz);
+
+ memset((void __iomem *)dst + phdr->p_filesz, 0,
+ phdr->p_memsz - phdr->p_filesz);
+}
+
+static int load_slim_core_fw(const struct firmware *fw, void *context)
+{
+ struct c8sectpfei *fei = context;
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ u8 __iomem *dst;
+ int err = 0, i;
+
+ if (!fw || !context)
+ return -EINVAL;
+
+ ehdr = (Elf32_Ehdr *)fw->data;
+ phdr = (Elf32_Phdr *)(fw->data + ehdr->e_phoff);
+
+ /* go through the available ELF segments */
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+
+ /* Only consider LOAD segments */
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ /*
+ * Check segment is contained within the fw->data buffer
+ */
+ if (phdr->p_offset + phdr->p_filesz > fw->size) {
+ dev_err(fei->dev,
+ "Segment %d is outside of firmware file\n", i);
+ err = -EINVAL;
+ break;
+ }
+
+ /*
+ * MEMDMA IMEM has executable flag set, otherwise load
+ * this segment into DMEM.
+ *
+ */
+
+ if (phdr->p_flags & PF_X) {
+ dst = (u8 __iomem *) fei->io + DMA_MEMDMA_IMEM;
+ /*
+ * The Slim ELF file uses 32-bit word addressing for
+ * load offsets.
+ */
+ dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
+ load_imem_segment(fei, phdr, fw, dst, i);
+ } else {
+ dst = (u8 __iomem *) fei->io + DMA_MEMDMA_DMEM;
+ /*
+ * The Slim ELF file uses 32-bit word addressing for
+ * load offsets.
+ */
+ dst += (phdr->p_paddr & 0xFFFFF) * sizeof(unsigned int);
+ load_dmem_segment(fei, phdr, fw, dst, i);
+ }
+ }
+
+ release_firmware(fw);
+ return err;
+}
+
+static void load_c8sectpfe_fw_cb(const struct firmware *fw, void *context)
+{
+ struct c8sectpfei *fei = context;
+ int err;
+
+ err = c8sectpfe_elf_sanity_check(fei, fw);
+ if (err) {
+ dev_err(fei->dev, "c8sectpfe_elf_sanity_check failed err=(%d)\n"
+ , err);
+ goto err;
+ }
+
+ err = load_slim_core_fw(fw, context);
+ if (err) {
+ dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err);
+ goto err;
+ }
+
+ /* now the firmware is loaded configure the input blocks */
+ err = configure_channels(fei);
+ if (err) {
+ dev_err(fei->dev, "configure_channels failed err=(%d)\n", err);
+ goto err;
+ }
+
+ /*
+ * STBus target port can access IMEM and DMEM ports
+ * without waiting for CPU
+ */
+ writel(0x1, fei->io + DMA_PER_STBUS_SYNC);
+
+ dev_info(fei->dev, "Boot the memdma SLIM core\n");
+ writel(0x1, fei->io + DMA_CPU_RUN);
+
+ atomic_set(&fei->fw_loaded, 1);
+err:
+ complete_all(&fei->fw_ack);
+}
+
+static int load_c8sectpfe_fw_step1(struct c8sectpfei *fei)
+{
+ int err;
+
+ dev_info(fei->dev, "Loading firmware: %s\n", FIRMWARE_MEMDMA);
+
+ init_completion(&fei->fw_ack);
+ atomic_set(&fei->fw_loaded, 0);
+
+ err = reject_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ FIRMWARE_MEMDMA, fei->dev, GFP_KERNEL, fei,
+ load_c8sectpfe_fw_cb);
+
+ if (err) {
+ dev_err(fei->dev, "request_firmware_nowait err: %d.\n", err);
+ complete_all(&fei->fw_ack);
+ return err;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id c8sectpfe_match[] = {
+ { .compatible = "st,stih407-c8sectpfe" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, c8sectpfe_match);
+
+static struct platform_driver c8sectpfe_driver = {
+ .driver = {
+ .name = "c8sectpfe",
+ .of_match_table = of_match_ptr(c8sectpfe_match),
+ },
+ .probe = c8sectpfe_probe,
+ .remove = c8sectpfe_remove,
+};
+
+module_platform_driver(c8sectpfe_driver);
+
+MODULE_AUTHOR("Peter Bennett <peter.bennett@st.com>");
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_DESCRIPTION("C8SECTPFE STi DVB Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
new file mode 100644
index 000000000..39e7a221a
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
@@ -0,0 +1,288 @@
+/*
+ * c8sectpfe-core.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author:Peter Bennett <peter.bennett@st.com>
+ * Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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 _C8SECTPFE_CORE_H_
+#define _C8SECTPFE_CORE_H_
+
+#define C8SECTPFEI_MAXCHANNEL 16
+#define C8SECTPFEI_MAXADAPTER 3
+
+#define C8SECTPFE_MAX_TSIN_CHAN 8
+
+struct channel_info {
+
+ int tsin_id;
+ bool invert_ts_clk;
+ bool serial_not_parallel;
+ bool async_not_sync;
+ int i2c;
+ int dvb_card;
+
+ int rst_gpio;
+
+ struct i2c_adapter *i2c_adapter;
+ struct i2c_adapter *tuner_i2c;
+ struct i2c_adapter *lnb_i2c;
+ struct i2c_client *i2c_client;
+ struct dvb_frontend *frontend;
+
+ struct pinctrl_state *pstate;
+
+ int demux_mapping;
+ int active;
+
+ void *back_buffer_start;
+ void *back_buffer_aligned;
+ dma_addr_t back_buffer_busaddr;
+
+ void *pid_buffer_start;
+ void *pid_buffer_aligned;
+ dma_addr_t pid_buffer_busaddr;
+
+ unsigned long fifo;
+
+ struct completion idle_completion;
+ struct tasklet_struct tsklet;
+
+ struct c8sectpfei *fei;
+ void __iomem *irec;
+
+};
+
+struct c8sectpfe_hw {
+ int num_ib;
+ int num_mib;
+ int num_swts;
+ int num_tsout;
+ int num_ccsc;
+ int num_ram;
+ int num_tp;
+};
+
+struct c8sectpfei {
+
+ struct device *dev;
+ struct pinctrl *pinctrl;
+
+ struct dentry *root;
+ struct debugfs_regset32 *regset;
+ struct completion fw_ack;
+ atomic_t fw_loaded;
+
+ int tsin_count;
+
+ struct c8sectpfe_hw hw_stats;
+
+ struct c8sectpfe *c8sectpfe[C8SECTPFEI_MAXADAPTER];
+
+ int mapping[C8SECTPFEI_MAXCHANNEL];
+
+ struct mutex lock;
+
+ struct timer_list timer; /* timer interrupts for outputs */
+
+ void __iomem *io;
+ void __iomem *sram;
+
+ unsigned long sram_size;
+
+ struct channel_info *channel_data[C8SECTPFE_MAX_TSIN_CHAN];
+
+ struct clk *c8sectpfeclk;
+ int nima_rst_gpio;
+ int nimb_rst_gpio;
+
+ int idle_irq;
+ int error_irq;
+
+ int global_feed_count;
+};
+
+/* C8SECTPFE SYS Regs list */
+
+#define SYS_INPUT_ERR_STATUS 0x0
+#define SYS_OTHER_ERR_STATUS 0x8
+#define SYS_INPUT_ERR_MASK 0x10
+#define SYS_OTHER_ERR_MASK 0x18
+#define SYS_DMA_ROUTE 0x20
+#define SYS_INPUT_CLKEN 0x30
+#define IBENABLE_MASK 0x7F
+
+#define SYS_OTHER_CLKEN 0x38
+#define TSDMAENABLE BIT(1)
+#define MEMDMAENABLE BIT(0)
+
+#define SYS_CFG_NUM_IB 0x200
+#define SYS_CFG_NUM_MIB 0x204
+#define SYS_CFG_NUM_SWTS 0x208
+#define SYS_CFG_NUM_TSOUT 0x20C
+#define SYS_CFG_NUM_CCSC 0x210
+#define SYS_CFG_NUM_RAM 0x214
+#define SYS_CFG_NUM_TP 0x218
+
+/* Input Block Regs */
+
+#define C8SECTPFE_INPUTBLK_OFFSET 0x1000
+#define C8SECTPFE_CHANNEL_OFFSET(x) ((x*0x40) + C8SECTPFE_INPUTBLK_OFFSET)
+
+#define C8SECTPFE_IB_IP_FMT_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x00)
+#define C8SECTPFE_IGNORE_ERR_AT_SOP BIT(7)
+#define C8SECTPFE_IGNORE_ERR_IN_PKT BIT(6)
+#define C8SECTPFE_IGNORE_ERR_IN_BYTE BIT(5)
+#define C8SECTPFE_INVERT_TSCLK BIT(4)
+#define C8SECTPFE_ALIGN_BYTE_SOP BIT(3)
+#define C8SECTPFE_ASYNC_NOT_SYNC BIT(2)
+#define C8SECTPFE_BYTE_ENDIANNESS_MSB BIT(1)
+#define C8SECTPFE_SERIAL_NOT_PARALLEL BIT(0)
+
+#define C8SECTPFE_IB_SYNCLCKDRP_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x04)
+#define C8SECTPFE_SYNC(x) (x & 0xf)
+#define C8SECTPFE_DROP(x) ((x<<4) & 0xf)
+#define C8SECTPFE_TOKEN(x) ((x<<8) & 0xff00)
+#define C8SECTPFE_SLDENDIANNESS BIT(16)
+
+#define C8SECTPFE_IB_TAGBYTES_CFG(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x08)
+#define C8SECTPFE_TAG_HEADER(x) (x << 16)
+#define C8SECTPFE_TAG_COUNTER(x) ((x<<1) & 0x7fff)
+#define C8SECTPFE_TAG_ENABLE BIT(0)
+
+#define C8SECTPFE_IB_PID_SET(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x0C)
+#define C8SECTPFE_PID_OFFSET(x) (x & 0x3f)
+#define C8SECTPFE_PID_NUMBITS(x) ((x << 6) & 0xfff)
+#define C8SECTPFE_PID_ENABLE BIT(31)
+
+#define C8SECTPFE_IB_PKT_LEN(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x10)
+
+#define C8SECTPFE_IB_BUFF_STRT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x14)
+#define C8SECTPFE_IB_BUFF_END(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x18)
+#define C8SECTPFE_IB_READ_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x1C)
+#define C8SECTPFE_IB_WRT_PNT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x20)
+
+#define C8SECTPFE_IB_PRI_THRLD(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x24)
+#define C8SECTPFE_PRI_VALUE(x) (x & 0x7fffff)
+#define C8SECTPFE_PRI_LOWPRI(x) ((x & 0xf) << 24)
+#define C8SECTPFE_PRI_HIGHPRI(x) ((x & 0xf) << 28)
+
+#define C8SECTPFE_IB_STAT(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x28)
+#define C8SECTPFE_STAT_FIFO_OVERFLOW(x) (x & 0x1)
+#define C8SECTPFE_STAT_BUFFER_OVERFLOW(x) (x & 0x2)
+#define C8SECTPFE_STAT_OUTOFORDERRP(x) (x & 0x4)
+#define C8SECTPFE_STAT_PID_OVERFLOW(x) (x & 0x8)
+#define C8SECTPFE_STAT_PKT_OVERFLOW(x) (x & 0x10)
+#define C8SECTPFE_STAT_ERROR_PACKETS(x) ((x >> 8) & 0xf)
+#define C8SECTPFE_STAT_SHORT_PACKETS(x) ((x >> 12) & 0xf)
+
+#define C8SECTPFE_IB_MASK(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x2C)
+#define C8SECTPFE_MASK_FIFO_OVERFLOW BIT(0)
+#define C8SECTPFE_MASK_BUFFER_OVERFLOW BIT(1)
+#define C8SECTPFE_MASK_OUTOFORDERRP(x) BIT(2)
+#define C8SECTPFE_MASK_PID_OVERFLOW(x) BIT(3)
+#define C8SECTPFE_MASK_PKT_OVERFLOW(x) BIT(4)
+#define C8SECTPFE_MASK_ERROR_PACKETS(x) ((x & 0xf) << 8)
+#define C8SECTPFE_MASK_SHORT_PACKETS(x) ((x & 0xf) >> 12)
+
+#define C8SECTPFE_IB_SYS(x) (C8SECTPFE_CHANNEL_OFFSET(x) + 0x30)
+#define C8SECTPFE_SYS_RESET BIT(1)
+#define C8SECTPFE_SYS_ENABLE BIT(0)
+
+/*
+ * Ponter record data structure required for each input block
+ * see Table 82 on page 167 of functional specification.
+ */
+
+#define DMA_PRDS_MEMBASE 0x0 /* Internal sram base address */
+#define DMA_PRDS_MEMTOP 0x4 /* Internal sram top address */
+
+/*
+ * TS packet size, including tag bytes added by input block,
+ * rounded up to the next multiple of 8 bytes. The packet size,
+ * including any tagging bytes and rounded up to the nearest
+ * multiple of 8 bytes must be less than 255 bytes.
+ */
+#define DMA_PRDS_PKTSIZE 0x8
+#define DMA_PRDS_TPENABLE 0xc
+
+#define TP0_OFFSET 0x10
+#define DMA_PRDS_BUSBASE_TP(x) ((0x10*x) + TP0_OFFSET)
+#define DMA_PRDS_BUSTOP_TP(x) ((0x10*x) + TP0_OFFSET + 0x4)
+#define DMA_PRDS_BUSWP_TP(x) ((0x10*x) + TP0_OFFSET + 0x8)
+#define DMA_PRDS_BUSRP_TP(x) ((0x10*x) + TP0_OFFSET + 0xc)
+
+#define DMA_PRDS_SIZE (0x20)
+
+#define DMA_MEMDMA_OFFSET 0x4000
+#define DMA_IMEM_OFFSET 0x0
+#define DMA_DMEM_OFFSET 0x4000
+#define DMA_CPU 0x8000
+#define DMA_PER_OFFSET 0xb000
+
+#define DMA_MEMDMA_DMEM (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET)
+#define DMA_MEMDMA_IMEM (DMA_MEMDMA_OFFSET + DMA_IMEM_OFFSET)
+
+/* XP70 Slim core regs */
+#define DMA_CPU_ID (DMA_MEMDMA_OFFSET + DMA_CPU + 0x0)
+#define DMA_CPU_VCR (DMA_MEMDMA_OFFSET + DMA_CPU + 0x4)
+#define DMA_CPU_RUN (DMA_MEMDMA_OFFSET + DMA_CPU + 0x8)
+#define DMA_CPU_CLOCKGATE (DMA_MEMDMA_OFFSET + DMA_CPU + 0xc)
+#define DMA_CPU_PC (DMA_MEMDMA_OFFSET + DMA_CPU + 0x20)
+
+/* Enable Interrupt for a IB */
+#define DMA_PER_TPn_DREQ_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd00)
+/* Ack interrupt by setting corresponding bit */
+#define DMA_PER_TPn_DACK_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xd80)
+#define DMA_PER_TPn_DREQ (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe00)
+#define DMA_PER_TPn_DACK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xe80)
+#define DMA_PER_DREQ_MODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf80)
+#define DMA_PER_STBUS_SYNC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf88)
+#define DMA_PER_STBUS_ACCESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf8c)
+#define DMA_PER_STBUS_ADDRESS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xf90)
+#define DMA_PER_IDLE_INT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfa8)
+#define DMA_PER_PRIORITY (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfac)
+#define DMA_PER_MAX_OPCODE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb0)
+#define DMA_PER_MAX_CHUNK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfb4)
+#define DMA_PER_PAGE_SIZE (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfbc)
+#define DMA_PER_MBOX_STATUS (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc0)
+#define DMA_PER_MBOX_SET (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfc8)
+#define DMA_PER_MBOX_CLEAR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd0)
+#define DMA_PER_MBOX_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfd8)
+#define DMA_PER_INJECT_PKT_SRC (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe0)
+#define DMA_PER_INJECT_PKT_DEST (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe4)
+#define DMA_PER_INJECT_PKT_ADDR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfe8)
+#define DMA_PER_INJECT_PKT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xfec)
+#define DMA_PER_PAT_PTR_INIT (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff0)
+#define DMA_PER_PAT_PTR (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff4)
+#define DMA_PER_SLEEP_MASK (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xff8)
+#define DMA_PER_SLEEP_COUNTER (DMA_MEMDMA_OFFSET + DMA_PER_OFFSET + 0xffc)
+/* #define DMA_RF_CPUREGn DMA_RFBASEADDR n=0 to 15) slim regsa */
+
+/* The following are from DMA_DMEM_BaseAddress */
+#define DMA_FIRMWARE_VERSION (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x0)
+#define DMA_PTRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x4)
+#define DMA_PTRREC_INPUT_OFFSET (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x8)
+#define DMA_ERRREC_BASE (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0xc)
+#define DMA_ERROR_RECORD(n) ((n*4) + DMA_ERRREC_BASE + 0x4)
+#define DMA_IDLE_REQ (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x10)
+#define IDLEREQ BIT(31)
+
+#define DMA_FIRMWARE_CONFIG (DMA_MEMDMA_OFFSET + DMA_DMEM_OFFSET + 0x14)
+
+/* Regs for PID Filter */
+
+#define PIDF_OFFSET 0x2800
+#define PIDF_BASE(n) ((n*4) + PIDF_OFFSET)
+#define PIDF_LEAK_ENABLE (PIDF_OFFSET + 0x100)
+#define PIDF_LEAK_STATUS (PIDF_OFFSET + 0x108)
+#define PIDF_LEAK_COUNT_RESET (PIDF_OFFSET + 0x110)
+#define PIDF_LEAK_COUNTER (PIDF_OFFSET + 0x114)
+
+#endif /* _C8SECTPFE_CORE_H_ */
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
new file mode 100644
index 000000000..e9ba13db4
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
@@ -0,0 +1,271 @@
+/*
+ * c8sectpfe-debugfs.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "c8sectpfe-debugfs.h"
+
+#define dump_register(nm ...) \
+{ \
+ .name = #nm, \
+ .offset = nm, \
+}
+
+static const struct debugfs_reg32 fei_sys_regs[] = {
+ dump_register(SYS_INPUT_ERR_STATUS),
+ dump_register(SYS_OTHER_ERR_STATUS),
+ dump_register(SYS_INPUT_ERR_MASK),
+ dump_register(SYS_DMA_ROUTE),
+ dump_register(SYS_INPUT_CLKEN),
+ dump_register(IBENABLE_MASK),
+ dump_register(SYS_OTHER_CLKEN),
+ dump_register(SYS_CFG_NUM_IB),
+ dump_register(SYS_CFG_NUM_MIB),
+ dump_register(SYS_CFG_NUM_SWTS),
+ dump_register(SYS_CFG_NUM_TSOUT),
+ dump_register(SYS_CFG_NUM_CCSC),
+ dump_register(SYS_CFG_NUM_RAM),
+ dump_register(SYS_CFG_NUM_TP),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(0)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(0)),
+ dump_register(C8SECTPFE_IB_PID_SET(0)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(0)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(0)),
+ dump_register(C8SECTPFE_IB_BUFF_END(0)),
+ dump_register(C8SECTPFE_IB_READ_PNT(0)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(0)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(0)),
+ dump_register(C8SECTPFE_IB_STAT(0)),
+ dump_register(C8SECTPFE_IB_MASK(0)),
+ dump_register(C8SECTPFE_IB_SYS(0)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(1)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(1)),
+ dump_register(C8SECTPFE_IB_PID_SET(1)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(1)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(1)),
+ dump_register(C8SECTPFE_IB_BUFF_END(1)),
+ dump_register(C8SECTPFE_IB_READ_PNT(1)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(1)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(1)),
+ dump_register(C8SECTPFE_IB_STAT(1)),
+ dump_register(C8SECTPFE_IB_MASK(1)),
+ dump_register(C8SECTPFE_IB_SYS(1)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(2)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(2)),
+ dump_register(C8SECTPFE_IB_PID_SET(2)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(2)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(2)),
+ dump_register(C8SECTPFE_IB_BUFF_END(2)),
+ dump_register(C8SECTPFE_IB_READ_PNT(2)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(2)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(2)),
+ dump_register(C8SECTPFE_IB_STAT(2)),
+ dump_register(C8SECTPFE_IB_MASK(2)),
+ dump_register(C8SECTPFE_IB_SYS(2)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(3)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(3)),
+ dump_register(C8SECTPFE_IB_PID_SET(3)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(3)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(3)),
+ dump_register(C8SECTPFE_IB_BUFF_END(3)),
+ dump_register(C8SECTPFE_IB_READ_PNT(3)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(3)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(3)),
+ dump_register(C8SECTPFE_IB_STAT(3)),
+ dump_register(C8SECTPFE_IB_MASK(3)),
+ dump_register(C8SECTPFE_IB_SYS(3)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(4)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(4)),
+ dump_register(C8SECTPFE_IB_PID_SET(4)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(4)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(4)),
+ dump_register(C8SECTPFE_IB_BUFF_END(4)),
+ dump_register(C8SECTPFE_IB_READ_PNT(4)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(4)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(4)),
+ dump_register(C8SECTPFE_IB_STAT(4)),
+ dump_register(C8SECTPFE_IB_MASK(4)),
+ dump_register(C8SECTPFE_IB_SYS(4)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(5)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(5)),
+ dump_register(C8SECTPFE_IB_PID_SET(5)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(5)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(5)),
+ dump_register(C8SECTPFE_IB_BUFF_END(5)),
+ dump_register(C8SECTPFE_IB_READ_PNT(5)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(5)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(5)),
+ dump_register(C8SECTPFE_IB_STAT(5)),
+ dump_register(C8SECTPFE_IB_MASK(5)),
+ dump_register(C8SECTPFE_IB_SYS(5)),
+
+ dump_register(C8SECTPFE_IB_IP_FMT_CFG(6)),
+ dump_register(C8SECTPFE_IB_TAGBYTES_CFG(6)),
+ dump_register(C8SECTPFE_IB_PID_SET(6)),
+ dump_register(C8SECTPFE_IB_PKT_LEN(6)),
+ dump_register(C8SECTPFE_IB_BUFF_STRT(6)),
+ dump_register(C8SECTPFE_IB_BUFF_END(6)),
+ dump_register(C8SECTPFE_IB_READ_PNT(6)),
+ dump_register(C8SECTPFE_IB_WRT_PNT(6)),
+ dump_register(C8SECTPFE_IB_PRI_THRLD(6)),
+ dump_register(C8SECTPFE_IB_STAT(6)),
+ dump_register(C8SECTPFE_IB_MASK(6)),
+ dump_register(C8SECTPFE_IB_SYS(6)),
+
+ dump_register(DMA_CPU_ID),
+ dump_register(DMA_CPU_VCR),
+ dump_register(DMA_CPU_RUN),
+ dump_register(DMA_CPU_PC),
+
+ dump_register(DMA_PER_TPn_DREQ_MASK),
+ dump_register(DMA_PER_TPn_DACK_SET),
+ dump_register(DMA_PER_TPn_DREQ),
+ dump_register(DMA_PER_TPn_DACK),
+ dump_register(DMA_PER_DREQ_MODE),
+ dump_register(DMA_PER_STBUS_SYNC),
+ dump_register(DMA_PER_STBUS_ACCESS),
+ dump_register(DMA_PER_STBUS_ADDRESS),
+ dump_register(DMA_PER_IDLE_INT),
+ dump_register(DMA_PER_PRIORITY),
+ dump_register(DMA_PER_MAX_OPCODE),
+ dump_register(DMA_PER_MAX_CHUNK),
+ dump_register(DMA_PER_PAGE_SIZE),
+ dump_register(DMA_PER_MBOX_STATUS),
+ dump_register(DMA_PER_MBOX_SET),
+ dump_register(DMA_PER_MBOX_CLEAR),
+ dump_register(DMA_PER_MBOX_MASK),
+ dump_register(DMA_PER_INJECT_PKT_SRC),
+ dump_register(DMA_PER_INJECT_PKT_DEST),
+ dump_register(DMA_PER_INJECT_PKT_ADDR),
+ dump_register(DMA_PER_INJECT_PKT),
+ dump_register(DMA_PER_PAT_PTR_INIT),
+ dump_register(DMA_PER_PAT_PTR),
+ dump_register(DMA_PER_SLEEP_MASK),
+ dump_register(DMA_PER_SLEEP_COUNTER),
+
+ dump_register(DMA_FIRMWARE_VERSION),
+ dump_register(DMA_PTRREC_BASE),
+ dump_register(DMA_PTRREC_INPUT_OFFSET),
+ dump_register(DMA_ERRREC_BASE),
+
+ dump_register(DMA_ERROR_RECORD(0)),
+ dump_register(DMA_ERROR_RECORD(1)),
+ dump_register(DMA_ERROR_RECORD(2)),
+ dump_register(DMA_ERROR_RECORD(3)),
+ dump_register(DMA_ERROR_RECORD(4)),
+ dump_register(DMA_ERROR_RECORD(5)),
+ dump_register(DMA_ERROR_RECORD(6)),
+ dump_register(DMA_ERROR_RECORD(7)),
+ dump_register(DMA_ERROR_RECORD(8)),
+ dump_register(DMA_ERROR_RECORD(9)),
+ dump_register(DMA_ERROR_RECORD(10)),
+ dump_register(DMA_ERROR_RECORD(11)),
+ dump_register(DMA_ERROR_RECORD(12)),
+ dump_register(DMA_ERROR_RECORD(13)),
+ dump_register(DMA_ERROR_RECORD(14)),
+ dump_register(DMA_ERROR_RECORD(15)),
+ dump_register(DMA_ERROR_RECORD(16)),
+ dump_register(DMA_ERROR_RECORD(17)),
+ dump_register(DMA_ERROR_RECORD(18)),
+ dump_register(DMA_ERROR_RECORD(19)),
+ dump_register(DMA_ERROR_RECORD(20)),
+ dump_register(DMA_ERROR_RECORD(21)),
+ dump_register(DMA_ERROR_RECORD(22)),
+
+ dump_register(DMA_IDLE_REQ),
+ dump_register(DMA_FIRMWARE_CONFIG),
+
+ dump_register(PIDF_BASE(0)),
+ dump_register(PIDF_BASE(1)),
+ dump_register(PIDF_BASE(2)),
+ dump_register(PIDF_BASE(3)),
+ dump_register(PIDF_BASE(4)),
+ dump_register(PIDF_BASE(5)),
+ dump_register(PIDF_BASE(6)),
+ dump_register(PIDF_BASE(7)),
+ dump_register(PIDF_BASE(8)),
+ dump_register(PIDF_BASE(9)),
+ dump_register(PIDF_BASE(10)),
+ dump_register(PIDF_BASE(11)),
+ dump_register(PIDF_BASE(12)),
+ dump_register(PIDF_BASE(13)),
+ dump_register(PIDF_BASE(14)),
+ dump_register(PIDF_BASE(15)),
+ dump_register(PIDF_BASE(16)),
+ dump_register(PIDF_BASE(17)),
+ dump_register(PIDF_BASE(18)),
+ dump_register(PIDF_BASE(19)),
+ dump_register(PIDF_BASE(20)),
+ dump_register(PIDF_BASE(21)),
+ dump_register(PIDF_BASE(22)),
+ dump_register(PIDF_LEAK_ENABLE),
+ dump_register(PIDF_LEAK_STATUS),
+ dump_register(PIDF_LEAK_COUNT_RESET),
+ dump_register(PIDF_LEAK_COUNTER),
+};
+
+void c8sectpfe_debugfs_init(struct c8sectpfei *fei)
+{
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir("c8sectpfe", NULL);
+ if (!root)
+ goto err;
+
+ fei->root = root;
+
+ fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL);
+ if (!fei->regset)
+ goto err;
+
+ fei->regset->regs = fei_sys_regs;
+ fei->regset->nregs = ARRAY_SIZE(fei_sys_regs);
+ fei->regset->base = fei->io;
+
+ file = debugfs_create_regset32("registers", S_IRUGO, root,
+ fei->regset);
+ if (!file) {
+ dev_err(fei->dev,
+ "%s not able to create 'registers' debugfs\n"
+ , __func__);
+ goto err;
+ }
+
+ return;
+
+err:
+ debugfs_remove_recursive(root);
+}
+
+void c8sectpfe_debugfs_exit(struct c8sectpfei *fei)
+{
+ debugfs_remove_recursive(fei->root);
+ fei->root = NULL;
+}
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
new file mode 100644
index 000000000..8af1ac137
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
@@ -0,0 +1,26 @@
+/**
+ * c8sectpfe-debugfs.h - C8SECTPFE STi DVB driver debugfs header
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Authors: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __C8SECTPFE_DEBUG_H
+#define __C8SECTPFE_DEBUG_H
+
+#include "c8sectpfe-core.h"
+
+void c8sectpfe_debugfs_init(struct c8sectpfei *);
+void c8sectpfe_debugfs_exit(struct c8sectpfei *);
+
+#endif /* __C8SECTPFE_DEBUG_H */
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
new file mode 100644
index 000000000..69d7fe447
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
@@ -0,0 +1,244 @@
+/*
+ * c8sectpfe-dvb.c - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ */
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include <dt-bindings/media/c8sectpfe.h>
+
+#include "c8sectpfe-common.h"
+#include "c8sectpfe-core.h"
+#include "c8sectpfe-dvb.h"
+
+#include "dvb-pll.h"
+#include "lnbh24.h"
+#include "stv0367.h"
+#include "stv0367_priv.h"
+#include "stv6110x.h"
+#include "stv090x.h"
+#include "tda18212.h"
+
+static inline const char *dvb_card_str(unsigned int c)
+{
+ switch (c) {
+ case STV0367_TDA18212_NIMA_1: return "STV0367_TDA18212_NIMA_1";
+ case STV0367_TDA18212_NIMA_2: return "STV0367_TDA18212_NIMA_2";
+ case STV0367_TDA18212_NIMB_1: return "STV0367_TDA18212_NIMB_1";
+ case STV0367_TDA18212_NIMB_2: return "STV0367_TDA18212_NIMB_2";
+ case STV0903_6110_LNB24_NIMA: return "STV0903_6110_LNB24_NIMA";
+ case STV0903_6110_LNB24_NIMB: return "STV0903_6110_LNB24_NIMB";
+ default: return "unknown dvb frontend card";
+ }
+}
+
+static struct stv090x_config stv090x_config = {
+ .device = STV0903,
+ .demod_mode = STV090x_SINGLE,
+ .clk_mode = STV090x_CLK_EXT,
+ .xtal = 16000000,
+ .address = 0x69,
+
+ .ts1_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+ .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS,
+
+ .repeater_level = STV090x_RPTLEVEL_64,
+
+ .tuner_init = NULL,
+ .tuner_set_mode = NULL,
+ .tuner_set_frequency = NULL,
+ .tuner_get_frequency = NULL,
+ .tuner_set_bandwidth = NULL,
+ .tuner_get_bandwidth = NULL,
+ .tuner_set_bbgain = NULL,
+ .tuner_get_bbgain = NULL,
+ .tuner_set_refclk = NULL,
+ .tuner_get_status = NULL,
+};
+
+static struct stv6110x_config stv6110x_config = {
+ .addr = 0x60,
+ .refclk = 16000000,
+};
+
+#define NIMA 0
+#define NIMB 1
+
+static struct stv0367_config stv0367_tda18212_config[] = {
+ {
+ .demod_address = 0x1c,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1d,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ }, {
+ .demod_address = 0x1e,
+ .xtal = 16000000,
+ .if_khz = 4500,
+ .if_iq_mode = FE_TER_NORMAL_IF_TUNER,
+ .ts_mode = STV0367_SERIAL_PUNCT_CLOCK,
+ .clk_pol = STV0367_CLOCKPOLARITY_DEFAULT,
+ },
+};
+
+static struct tda18212_config tda18212_conf = {
+ .if_dvbt_6 = 4150,
+ .if_dvbt_7 = 4150,
+ .if_dvbt_8 = 4500,
+ .if_dvbc = 5000,
+};
+
+int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
+ struct c8sectpfe *c8sectpfe,
+ struct channel_info *tsin, int chan_num)
+{
+ struct tda18212_config *tda18212;
+ struct stv6110x_devctl *fe2;
+ struct i2c_client *client;
+ struct i2c_board_info tda18212_info = {
+ .type = "tda18212",
+ .addr = 0x60,
+ };
+
+ if (!tsin)
+ return -EINVAL;
+
+ switch (tsin->dvb_card) {
+
+ case STV0367_TDA18212_NIMA_1:
+ case STV0367_TDA18212_NIMA_2:
+ case STV0367_TDA18212_NIMB_1:
+ case STV0367_TDA18212_NIMB_2:
+ if (tsin->dvb_card == STV0367_TDA18212_NIMA_1)
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[0],
+ tsin->i2c_adapter);
+ else if (tsin->dvb_card == STV0367_TDA18212_NIMB_1)
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[1],
+ tsin->i2c_adapter);
+ else
+ *fe = dvb_attach(stv0367ter_attach,
+ &stv0367_tda18212_config[2],
+ tsin->i2c_adapter);
+
+ if (!*fe) {
+ dev_err(c8sectpfe->device,
+ "%s: stv0367ter_attach failed for NIM card %s\n"
+ , __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ };
+
+ /*
+ * init the demod so that i2c gate_ctrl
+ * to the tuner works correctly
+ */
+ (*fe)->ops.init(*fe);
+
+ /* Allocate the tda18212 structure */
+ tda18212 = devm_kzalloc(c8sectpfe->device,
+ sizeof(struct tda18212_config),
+ GFP_KERNEL);
+ if (!tda18212) {
+ dev_err(c8sectpfe->device,
+ "%s: devm_kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(tda18212, &tda18212_conf,
+ sizeof(struct tda18212_config));
+
+ tda18212->fe = (*fe);
+
+ tda18212_info.platform_data = tda18212;
+
+ /* attach tuner */
+ request_module("tda18212");
+ client = i2c_new_device(tsin->i2c_adapter, &tda18212_info);
+ if (!client || !client->dev.driver) {
+ dvb_frontend_detach(*fe);
+ return -ENODEV;
+ }
+
+ if (!try_module_get(client->dev.driver->owner)) {
+ i2c_unregister_device(client);
+ dvb_frontend_detach(*fe);
+ return -ENODEV;
+ }
+
+ tsin->i2c_client = client;
+
+ break;
+
+ case STV0903_6110_LNB24_NIMA:
+ *fe = dvb_attach(stv090x_attach, &stv090x_config,
+ tsin->i2c_adapter, STV090x_DEMODULATOR_0);
+ if (!*fe) {
+ dev_err(c8sectpfe->device, "%s: stv090x_attach failed\n"
+ "\tfor NIM card %s\n",
+ __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ }
+
+ fe2 = dvb_attach(stv6110x_attach, *fe,
+ &stv6110x_config, tsin->i2c_adapter);
+ if (!fe2) {
+ dev_err(c8sectpfe->device,
+ "%s: stv6110x_attach failed for NIM card %s\n"
+ , __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ };
+
+ stv090x_config.tuner_init = fe2->tuner_init;
+ stv090x_config.tuner_set_mode = fe2->tuner_set_mode;
+ stv090x_config.tuner_set_frequency = fe2->tuner_set_frequency;
+ stv090x_config.tuner_get_frequency = fe2->tuner_get_frequency;
+ stv090x_config.tuner_set_bandwidth = fe2->tuner_set_bandwidth;
+ stv090x_config.tuner_get_bandwidth = fe2->tuner_get_bandwidth;
+ stv090x_config.tuner_set_bbgain = fe2->tuner_set_bbgain;
+ stv090x_config.tuner_get_bbgain = fe2->tuner_get_bbgain;
+ stv090x_config.tuner_set_refclk = fe2->tuner_set_refclk;
+ stv090x_config.tuner_get_status = fe2->tuner_get_status;
+
+ dvb_attach(lnbh24_attach, *fe, tsin->i2c_adapter, 0, 0, 0x9);
+ break;
+
+ default:
+ dev_err(c8sectpfe->device,
+ "%s: DVB frontend card %s not yet supported\n",
+ __func__, dvb_card_str(tsin->dvb_card));
+ return -ENODEV;
+ }
+
+ (*fe)->id = chan_num;
+
+ dev_info(c8sectpfe->device,
+ "DVB frontend card %s successfully attached",
+ dvb_card_str(tsin->dvb_card));
+ return 0;
+}
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h
new file mode 100644
index 000000000..bd366dbc8
--- /dev/null
+++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h
@@ -0,0 +1,20 @@
+/*
+ * c8sectpfe-common.h - C8SECTPFE STi DVB driver
+ *
+ * Copyright (c) STMicroelectronics 2015
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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 _C8SECTPFE_DVB_H_
+#define _C8SECTPFE_DVB_H_
+
+int c8sectpfe_frontend_attach(struct dvb_frontend **fe,
+ struct c8sectpfe *c8sectpfe, struct channel_info *tsin,
+ int chan_num);
+
+#endif
diff --git a/drivers/media/platform/vivid/vivid-vid-cap.c b/drivers/media/platform/vivid/vivid-vid-cap.c
index c4268d1b2..ed0b8788a 100644
--- a/drivers/media/platform/vivid/vivid-vid-cap.c
+++ b/drivers/media/platform/vivid/vivid-vid-cap.c
@@ -1627,7 +1627,7 @@ static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
h_freq = (u32)bt->pixelclock / total_h_pixel;
if (bt->standards == 0 || (bt->standards & V4L2_DV_BT_STD_CVT)) {
- if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync,
+ if (v4l2_detect_cvt(total_v_lines, h_freq, bt->vsync, bt->width,
bt->polarities, bt->interlaced, timings))
return true;
}
diff --git a/drivers/media/platform/vivid/vivid-vid-out.c b/drivers/media/platform/vivid/vivid-vid-out.c
index 0862c1f24..c404e275e 100644
--- a/drivers/media/platform/vivid/vivid-vid-out.c
+++ b/drivers/media/platform/vivid/vivid-vid-out.c
@@ -1124,15 +1124,26 @@ int vivid_vid_out_s_std(struct file *file, void *priv, v4l2_std_id id)
return 0;
}
+static bool valid_cvt_gtf_timings(struct v4l2_dv_timings *timings)
+{
+ struct v4l2_bt_timings *bt = &timings->bt;
+
+ if ((bt->standards & (V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF)) &&
+ v4l2_valid_dv_timings(timings, &vivid_dv_timings_cap, NULL, NULL))
+ return true;
+
+ return false;
+}
+
int vivid_vid_out_s_dv_timings(struct file *file, void *_fh,
struct v4l2_dv_timings *timings)
{
struct vivid_dev *dev = video_drvdata(file);
-
if (!vivid_is_hdmi_out(dev))
return -ENODATA;
if (!v4l2_find_dv_timings_cap(timings, &vivid_dv_timings_cap,
- 0, NULL, NULL))
+ 0, NULL, NULL) &&
+ !valid_cvt_gtf_timings(timings))
return -EINVAL;
if (v4l2_match_dv_timings(timings, &dev->dv_timings_out, 0))
return 0;
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c
index 913485a90..4e6188638 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/vsp1/vsp1_drv.c
@@ -1,7 +1,7 @@
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -403,7 +403,10 @@ static int vsp1_pm_suspend(struct device *dev)
if (vsp1->ref_count == 0)
return 0;
+ vsp1_pipelines_suspend(vsp1);
+
clk_disable_unprepare(vsp1->clock);
+
return 0;
}
@@ -413,10 +416,14 @@ static int vsp1_pm_resume(struct device *dev)
WARN_ON(mutex_is_locked(&vsp1->lock));
- if (vsp1->ref_count)
+ if (vsp1->ref_count == 0)
return 0;
- return clk_prepare_enable(vsp1->clock);
+ clk_prepare_enable(vsp1->clock);
+
+ vsp1_pipelines_resume(vsp1);
+
+ return 0;
}
#endif
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/vsp1/vsp1_entity.c
index a453bb4dd..fd95a75b0 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/vsp1/vsp1_entity.c
@@ -24,22 +24,24 @@
bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
{
+ unsigned long flags;
bool streaming;
- mutex_lock(&entity->lock);
+ spin_lock_irqsave(&entity->lock, flags);
streaming = entity->streaming;
- mutex_unlock(&entity->lock);
+ spin_unlock_irqrestore(&entity->lock, flags);
return streaming;
}
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
{
+ unsigned long flags;
int ret;
- mutex_lock(&entity->lock);
+ spin_lock_irqsave(&entity->lock, flags);
entity->streaming = streaming;
- mutex_unlock(&entity->lock);
+ spin_unlock_irqrestore(&entity->lock, flags);
if (!streaming)
return 0;
@@ -49,9 +51,9 @@ int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
if (ret < 0) {
- mutex_lock(&entity->lock);
+ spin_lock_irqsave(&entity->lock, flags);
entity->streaming = false;
- mutex_unlock(&entity->lock);
+ spin_unlock_irqrestore(&entity->lock, flags);
}
return ret;
@@ -193,7 +195,7 @@ int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
if (i == ARRAY_SIZE(vsp1_routes))
return -EINVAL;
- mutex_init(&entity->lock);
+ spin_lock_init(&entity->lock);
entity->vsp1 = vsp1;
entity->source_pad = num_pads - 1;
@@ -228,6 +230,4 @@ void vsp1_entity_destroy(struct vsp1_entity *entity)
if (entity->subdev.ctrl_handler)
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
media_entity_cleanup(&entity->subdev.entity);
-
- mutex_destroy(&entity->lock);
}
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/vsp1/vsp1_entity.h
index 62c768d1c..8867a5787 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/vsp1/vsp1_entity.h
@@ -14,7 +14,7 @@
#define __VSP1_ENTITY_H__
#include <linux/list.h>
-#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <media/v4l2-subdev.h>
@@ -73,7 +73,7 @@ struct vsp1_entity {
struct vsp1_video *video;
- struct mutex lock; /* Protects the streaming field */
+ spinlock_t lock; /* Protects the streaming field */
bool streaming;
};
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h
index da3c573e1..25b48738b 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/vsp1/vsp1_regs.h
@@ -238,7 +238,7 @@
#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_MASK (0xfff << 0)
#define VI6_WPF_SZCLIP_SIZE_SHIFT 0
#define VI6_WPF_OUTFMT 0x100c
@@ -304,9 +304,9 @@
#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_MASK (0xff << 16)
#define VI6_DPR_ROUTE_FXA_SHIFT 16
-#define VI6_DPR_ROUTE_FP_MASK (0xff << 8)
+#define VI6_DPR_ROUTE_FP_MASK (0x3f << 8)
#define VI6_DPR_ROUTE_FP_SHIFT 8
#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0)
#define VI6_DPR_ROUTE_RT_SHIFT 0
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/vsp1/vsp1_rwpf.c
index fa71f4695..9688c219b 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/vsp1/vsp1_rwpf.c
@@ -197,6 +197,17 @@ int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
*/
format = vsp1_entity_get_pad_format(&rwpf->entity, cfg, RWPF_PAD_SINK,
sel->which);
+
+ /* Restrict the crop rectangle coordinates to multiples of 2 to avoid
+ * shifting the color plane.
+ */
+ if (format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
+ sel->r.left = ALIGN(sel->r.left, 2);
+ sel->r.top = ALIGN(sel->r.top, 2);
+ sel->r.width = round_down(sel->r.width, 2);
+ sel->r.height = round_down(sel->r.height, 2);
+ }
+
sel->r.left = min_t(unsigned int, sel->r.left, format->width - 2);
sel->r.top = min_t(unsigned int, sel->r.top, format->height - 2);
if (rwpf->entity.type == VSP1_ENTITY_WPF) {
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c
index d91f19a9e..3c124c14c 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/vsp1/vsp1_video.c
@@ -1,7 +1,7 @@
/*
* vsp1_video.c -- R-Car VSP1 Video Node
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -245,7 +245,7 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
* the datasheet, strides not aligned to a multiple of 128 bytes result
* in image corruption.
*/
- for (i = 0; i < max(info->planes, 2U); ++i) {
+ for (i = 0; i < min(info->planes, 2U); ++i) {
unsigned int hsub = i > 0 ? info->hsub : 1;
unsigned int vsub = i > 0 ? info->vsub : 1;
unsigned int align = 128;
@@ -514,6 +514,18 @@ static void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
pipe->buffers_ready = 0;
}
+static bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe)
+{
+ unsigned long flags;
+ bool stopped;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ stopped = pipe->state == VSP1_PIPELINE_STOPPED,
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+
+ return stopped;
+}
+
static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
{
struct vsp1_entity *entity;
@@ -525,7 +537,7 @@ static int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
pipe->state = VSP1_PIPELINE_STOPPING;
spin_unlock_irqrestore(&pipe->irqlock, flags);
- ret = wait_event_timeout(pipe->wq, pipe->state == VSP1_PIPELINE_STOPPED,
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
msecs_to_jiffies(500));
ret = ret == 0 ? -ETIMEDOUT : 0;
@@ -703,6 +715,73 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
}
}
+void vsp1_pipelines_suspend(struct vsp1_device *vsp1)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+
+ /* To avoid increasing the system suspend time needlessly, loop over the
+ * pipelines twice, first to set them all to the stopping state, and then
+ * to wait for the stop to complete.
+ */
+ for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ spin_lock_irqsave(&pipe->irqlock, flags);
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ spin_unlock_irqrestore(&pipe->irqlock, flags);
+ }
+
+ for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev, "pipeline %u stop timeout\n",
+ wpf->entity.index);
+ }
+}
+
+void vsp1_pipelines_resume(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+
+ /* Resume pipeline all running pipelines. */
+ for (i = 0; i < vsp1->pdata.wpf_count; ++i) {
+ struct vsp1_rwpf *wpf = vsp1->wpf[i];
+ struct vsp1_pipeline *pipe;
+
+ if (wpf == NULL)
+ continue;
+
+ pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
+ if (pipe == NULL)
+ continue;
+
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_pipeline_run(pipe);
+ }
+}
+
/* -----------------------------------------------------------------------------
* videobuf2 Queue Operations
*/
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/vsp1/vsp1_video.h
index fd2851a82..0887a4d27 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/vsp1/vsp1_video.h
@@ -1,7 +1,7 @@
/*
* vsp1_video.h -- R-Car VSP1 Video Node
*
- * Copyright (C) 2013-2014 Renesas Electronics Corporation
+ * Copyright (C) 2013-2015 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
@@ -149,4 +149,7 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
unsigned int alpha);
+void vsp1_pipelines_suspend(struct vsp1_device *vsp1);
+void vsp1_pipelines_resume(struct vsp1_device *vsp1);
+
#endif /* __VSP1_VIDEO_H__ */