Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,6 @@
vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_video.o
vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o
vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o

View file

@ -0,0 +1,82 @@
/*
* vsp1.h -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_H__
#define __VSP1_H__
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/platform_data/vsp1.h>
#include <media/media-device.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
#include "vsp1_regs.h"
struct clk;
struct device;
struct vsp1_platform_data;
struct vsp1_bru;
struct vsp1_hsit;
struct vsp1_lif;
struct vsp1_lut;
struct vsp1_rwpf;
struct vsp1_sru;
struct vsp1_uds;
#define VSP1_MAX_RPF 5
#define VSP1_MAX_UDS 3
#define VSP1_MAX_WPF 4
struct vsp1_device {
struct device *dev;
struct vsp1_platform_data *pdata;
void __iomem *mmio;
struct clk *clock;
struct mutex lock;
int ref_count;
struct vsp1_bru *bru;
struct vsp1_hsit *hsi;
struct vsp1_hsit *hst;
struct vsp1_lif *lif;
struct vsp1_lut *lut;
struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
struct vsp1_sru *sru;
struct vsp1_uds *uds[VSP1_MAX_UDS];
struct vsp1_rwpf *wpf[VSP1_MAX_WPF];
struct list_head entities;
struct v4l2_device v4l2_dev;
struct media_device media_dev;
};
int vsp1_device_get(struct vsp1_device *vsp1);
void vsp1_device_put(struct vsp1_device *vsp1);
static inline u32 vsp1_read(struct vsp1_device *vsp1, u32 reg)
{
return ioread32(vsp1->mmio + reg);
}
static inline void vsp1_write(struct vsp1_device *vsp1, u32 reg, u32 data)
{
iowrite32(data, vsp1->mmio + reg);
}
#endif /* __VSP1_H__ */

View file

@ -0,0 +1,452 @@
/*
* vsp1_bru.c -- R-Car VSP1 Blend ROP Unit
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_rwpf.h"
#define BRU_MIN_SIZE 4U
#define BRU_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_bru_read(struct vsp1_bru *bru, u32 reg)
{
return vsp1_read(bru->entity.vsp1, reg);
}
static inline void vsp1_bru_write(struct vsp1_bru *bru, u32 reg, u32 data)
{
vsp1_write(bru->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
static int bru_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_bru *bru =
container_of(ctrl->handler, struct vsp1_bru, ctrls);
if (!vsp1_entity_is_streaming(&bru->entity))
return 0;
switch (ctrl->id) {
case V4L2_CID_BG_COLOR:
vsp1_bru_write(bru, VI6_BRU_VIRRPF_COL, ctrl->val |
(0xff << VI6_BRU_VIRRPF_COL_A_SHIFT));
break;
}
return 0;
}
static const struct v4l2_ctrl_ops bru_ctrl_ops = {
.s_ctrl = bru_s_ctrl,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int bru_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
unsigned int flags;
unsigned int i;
int ret;
ret = vsp1_entity_set_streaming(&bru->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
format = &bru->entity.formats[BRU_PAD_SOURCE];
/* The hardware is extremely flexible but we have no userspace API to
* expose all the parameters, nor is it clear whether we would have use
* cases for all the supported modes. Let's just harcode the parameters
* to sane default values for now.
*/
/* Disable dithering and enable color data normalization unless the
* format at the pipeline output is premultiplied.
*/
flags = pipe->output ? pipe->output->video.format.flags : 0;
vsp1_bru_write(bru, VI6_BRU_INCTRL,
flags & V4L2_PIX_FMT_FLAG_PREMUL_ALPHA ?
0 : VI6_BRU_INCTRL_NRM);
/* Set the background position to cover the whole output image. */
vsp1_bru_write(bru, VI6_BRU_VIRRPF_SIZE,
(format->width << VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT) |
(format->height << VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT));
vsp1_bru_write(bru, VI6_BRU_VIRRPF_LOC, 0);
/* Route BRU input 1 as SRC input to the ROP unit and configure the ROP
* unit with a NOP operation to make BRU input 1 available as the
* Blend/ROP unit B SRC input.
*/
vsp1_bru_write(bru, VI6_BRU_ROP, VI6_BRU_ROP_DSTSEL_BRUIN(1) |
VI6_BRU_ROP_CROP(VI6_ROP_NOP) |
VI6_BRU_ROP_AROP(VI6_ROP_NOP));
for (i = 0; i < 4; ++i) {
bool premultiplied = false;
u32 ctrl = 0;
/* Configure all Blend/ROP units corresponding to an enabled BRU
* input for alpha blending. Blend/ROP units corresponding to
* disabled BRU inputs are used in ROP NOP mode to ignore the
* SRC input.
*/
if (bru->inputs[i].rpf) {
ctrl |= VI6_BRU_CTRL_RBC;
premultiplied = bru->inputs[i].rpf->video.format.flags
& V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
} else {
ctrl |= VI6_BRU_CTRL_CROP(VI6_ROP_NOP)
| VI6_BRU_CTRL_AROP(VI6_ROP_NOP);
}
/* Select the virtual RPF as the Blend/ROP unit A DST input to
* serve as a background color.
*/
if (i == 0)
ctrl |= VI6_BRU_CTRL_DSTSEL_VRPF;
/* Route BRU inputs 0 to 3 as SRC inputs to Blend/ROP units A to
* D in that order. The Blend/ROP unit B SRC is hardwired to the
* ROP unit output, the corresponding register bits must be set
* to 0.
*/
if (i != 1)
ctrl |= VI6_BRU_CTRL_SRCSEL_BRUIN(i);
vsp1_bru_write(bru, VI6_BRU_CTRL(i), ctrl);
/* Harcode the blending formula to
*
* DSTc = DSTc * (1 - SRCa) + SRCc * SRCa
* DSTa = DSTa * (1 - SRCa) + SRCa
*
* when the SRC input isn't premultiplied, and to
*
* DSTc = DSTc * (1 - SRCa) + SRCc
* DSTa = DSTa * (1 - SRCa) + SRCa
*
* otherwise.
*/
vsp1_bru_write(bru, VI6_BRU_BLD(i),
VI6_BRU_BLD_CCMDX_255_SRC_A |
(premultiplied ? VI6_BRU_BLD_CCMDY_COEFY :
VI6_BRU_BLD_CCMDY_SRC_A) |
VI6_BRU_BLD_ACMDX_255_SRC_A |
VI6_BRU_BLD_ACMDY_COEFY |
(0xff << VI6_BRU_BLD_COEFY_SHIFT));
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
/*
* The BRU can't perform format conversion, all sink and source formats must be
* identical. We pick the format on the first sink pad (pad 0) and propagate it
* to all other pads.
*/
static int bru_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
if (code->pad == BRU_PAD_SINK(0)) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, BRU_PAD_SINK(0));
code->code = format->code;
}
return 0;
}
static int bru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index)
return -EINVAL;
if (fse->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fse->code != V4L2_MBUS_FMT_AYUV8_1X32)
return -EINVAL;
fse->min_width = BRU_MIN_SIZE;
fse->max_width = BRU_MAX_SIZE;
fse->min_height = BRU_MIN_SIZE;
fse->max_height = BRU_MAX_SIZE;
return 0;
}
static struct v4l2_rect *bru_get_compose(struct vsp1_bru *bru,
struct v4l2_subdev_fh *fh,
unsigned int pad, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(fh, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &bru->inputs[pad].compose;
default:
return NULL;
}
}
static int bru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_bru *bru = to_bru(subdev);
fmt->format = *vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static void bru_try_format(struct vsp1_bru *bru, struct v4l2_subdev_fh *fh,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
switch (pad) {
case BRU_PAD_SINK(0):
/* Default to YUV if the requested format is not supported. */
if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
break;
default:
/* The BRU can't perform format conversion. */
format = vsp1_entity_get_pad_format(&bru->entity, fh,
BRU_PAD_SINK(0), which);
fmt->code = format->code;
break;
}
fmt->width = clamp(fmt->width, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->height = clamp(fmt->height, BRU_MIN_SIZE, BRU_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int bru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
bru_try_format(bru, fh, fmt->pad, &fmt->format, fmt->which);
format = vsp1_entity_get_pad_format(&bru->entity, fh, fmt->pad,
fmt->which);
*format = fmt->format;
/* Reset the compose rectangle */
if (fmt->pad != BRU_PAD_SOURCE) {
struct v4l2_rect *compose;
compose = bru_get_compose(bru, fh, fmt->pad, fmt->which);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
compose->height = format->height;
}
/* Propagate the format code to all pads */
if (fmt->pad == BRU_PAD_SINK(0)) {
unsigned int i;
for (i = 0; i <= BRU_PAD_SOURCE; ++i) {
format = vsp1_entity_get_pad_format(&bru->entity, fh,
i, fmt->which);
format->code = fmt->format.code;
}
}
return 0;
}
static int bru_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
if (sel->pad == BRU_PAD_SOURCE)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = BRU_MAX_SIZE;
sel->r.height = BRU_MAX_SIZE;
return 0;
case V4L2_SEL_TGT_COMPOSE:
sel->r = *bru_get_compose(bru, fh, sel->pad, sel->which);
return 0;
default:
return -EINVAL;
}
}
static int bru_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct vsp1_bru *bru = to_bru(subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
if (sel->pad == BRU_PAD_SOURCE)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
/* The compose rectangle top left corner must be inside the output
* frame.
*/
format = vsp1_entity_get_pad_format(&bru->entity, fh, BRU_PAD_SOURCE,
sel->which);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
/* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
format = vsp1_entity_get_pad_format(&bru->entity, fh, sel->pad,
sel->which);
sel->r.width = format->width;
sel->r.height = format->height;
compose = bru_get_compose(bru, fh, sel->pad, sel->which);
*compose = sel->r;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops bru_video_ops = {
.s_stream = bru_s_stream,
};
static struct v4l2_subdev_pad_ops bru_pad_ops = {
.enum_mbus_code = bru_enum_mbus_code,
.enum_frame_size = bru_enum_frame_size,
.get_fmt = bru_get_format,
.set_fmt = bru_set_format,
.get_selection = bru_get_selection,
.set_selection = bru_set_selection,
};
static struct v4l2_subdev_ops bru_ops = {
.video = &bru_video_ops,
.pad = &bru_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_bru *bru;
int ret;
bru = devm_kzalloc(vsp1->dev, sizeof(*bru), GFP_KERNEL);
if (bru == NULL)
return ERR_PTR(-ENOMEM);
bru->entity.type = VSP1_ENTITY_BRU;
ret = vsp1_entity_init(vsp1, &bru->entity, 5);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &bru->entity.subdev;
v4l2_subdev_init(subdev, &bru_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s bru",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, bru);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&bru->ctrls, 1);
v4l2_ctrl_new_std(&bru->ctrls, &bru_ctrl_ops, V4L2_CID_BG_COLOR,
0, 0xffffff, 1, 0);
bru->entity.subdev.ctrl_handler = &bru->ctrls;
if (bru->ctrls.error) {
dev_err(vsp1->dev, "bru: failed to initialize controls\n");
ret = bru->ctrls.error;
vsp1_entity_destroy(&bru->entity);
return ERR_PTR(ret);
}
return bru;
}

View file

@ -0,0 +1,46 @@
/*
* vsp1_bru.h -- R-Car VSP1 Blend ROP Unit
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_BRU_H__
#define __VSP1_BRU_H__
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
struct vsp1_rwpf;
#define BRU_PAD_SINK(n) (n)
#define BRU_PAD_SOURCE 4
struct vsp1_bru {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
struct {
struct vsp1_rwpf *rpf;
struct v4l2_rect compose;
} inputs[4];
};
static inline struct vsp1_bru *to_bru(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_bru, entity.subdev);
}
struct vsp1_bru *vsp1_bru_create(struct vsp1_device *vsp1);
#endif /* __VSP1_BRU_H__ */

View file

@ -0,0 +1,577 @@
/*
* vsp1_drv.c -- R-Car VSP1 Driver
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include "vsp1.h"
#include "vsp1_bru.h"
#include "vsp1_hsit.h"
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
/* -----------------------------------------------------------------------------
* Interrupt Handling
*/
static irqreturn_t vsp1_irq_handler(int irq, void *data)
{
u32 mask = VI6_WFP_IRQ_STA_DFE | VI6_WFP_IRQ_STA_FRE;
struct vsp1_device *vsp1 = data;
irqreturn_t ret = IRQ_NONE;
unsigned int i;
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
struct vsp1_rwpf *wpf = vsp1->wpf[i];
struct vsp1_pipeline *pipe;
u32 status;
if (wpf == NULL)
continue;
pipe = to_vsp1_pipeline(&wpf->entity.subdev.entity);
status = vsp1_read(vsp1, VI6_WPF_IRQ_STA(i));
vsp1_write(vsp1, VI6_WPF_IRQ_STA(i), ~status & mask);
if (status & VI6_WFP_IRQ_STA_FRE) {
vsp1_pipeline_frame_end(pipe);
ret = IRQ_HANDLED;
}
}
return ret;
}
/* -----------------------------------------------------------------------------
* Entities
*/
/*
* vsp1_create_links - Create links from all sources to the given sink
*
* This function creates media links from all valid sources to the given sink
* pad. Links that would be invalid according to the VSP1 hardware capabilities
* are skipped. Those include all links
*
* - from a UDS to a UDS (UDS entities can't be chained)
* - from an entity to itself (no loops are allowed)
*/
static int vsp1_create_links(struct vsp1_device *vsp1, struct vsp1_entity *sink)
{
struct media_entity *entity = &sink->subdev.entity;
struct vsp1_entity *source;
unsigned int pad;
int ret;
list_for_each_entry(source, &vsp1->entities, list_dev) {
u32 flags;
if (source->type == sink->type)
continue;
if (source->type == VSP1_ENTITY_LIF ||
source->type == VSP1_ENTITY_WPF)
continue;
flags = source->type == VSP1_ENTITY_RPF &&
sink->type == VSP1_ENTITY_WPF &&
source->index == sink->index
? MEDIA_LNK_FL_ENABLED : 0;
for (pad = 0; pad < entity->num_pads; ++pad) {
if (!(entity->pads[pad].flags & MEDIA_PAD_FL_SINK))
continue;
ret = media_entity_create_link(&source->subdev.entity,
source->source_pad,
entity, pad, flags);
if (ret < 0)
return ret;
if (flags & MEDIA_LNK_FL_ENABLED)
source->sink = entity;
}
}
return 0;
}
static void vsp1_destroy_entities(struct vsp1_device *vsp1)
{
struct vsp1_entity *entity;
struct vsp1_entity *next;
list_for_each_entry_safe(entity, next, &vsp1->entities, list_dev) {
list_del(&entity->list_dev);
vsp1_entity_destroy(entity);
}
v4l2_device_unregister(&vsp1->v4l2_dev);
media_device_unregister(&vsp1->media_dev);
}
static int vsp1_create_entities(struct vsp1_device *vsp1)
{
struct media_device *mdev = &vsp1->media_dev;
struct v4l2_device *vdev = &vsp1->v4l2_dev;
struct vsp1_entity *entity;
unsigned int i;
int ret;
mdev->dev = vsp1->dev;
strlcpy(mdev->model, "VSP1", sizeof(mdev->model));
snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s",
dev_name(mdev->dev));
ret = media_device_register(mdev);
if (ret < 0) {
dev_err(vsp1->dev, "media device registration failed (%d)\n",
ret);
return ret;
}
vdev->mdev = mdev;
ret = v4l2_device_register(vsp1->dev, vdev);
if (ret < 0) {
dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n",
ret);
goto done;
}
/* Instantiate all the entities. */
vsp1->bru = vsp1_bru_create(vsp1);
if (IS_ERR(vsp1->bru)) {
ret = PTR_ERR(vsp1->bru);
goto done;
}
list_add_tail(&vsp1->bru->entity.list_dev, &vsp1->entities);
vsp1->hsi = vsp1_hsit_create(vsp1, true);
if (IS_ERR(vsp1->hsi)) {
ret = PTR_ERR(vsp1->hsi);
goto done;
}
list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
vsp1->hst = vsp1_hsit_create(vsp1, false);
if (IS_ERR(vsp1->hst)) {
ret = PTR_ERR(vsp1->hst);
goto done;
}
list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
if (vsp1->pdata->features & VSP1_HAS_LIF) {
vsp1->lif = vsp1_lif_create(vsp1);
if (IS_ERR(vsp1->lif)) {
ret = PTR_ERR(vsp1->lif);
goto done;
}
list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities);
}
if (vsp1->pdata->features & VSP1_HAS_LUT) {
vsp1->lut = vsp1_lut_create(vsp1);
if (IS_ERR(vsp1->lut)) {
ret = PTR_ERR(vsp1->lut);
goto done;
}
list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->rpf_count; ++i) {
struct vsp1_rwpf *rpf;
rpf = vsp1_rpf_create(vsp1, i);
if (IS_ERR(rpf)) {
ret = PTR_ERR(rpf);
goto done;
}
vsp1->rpf[i] = rpf;
list_add_tail(&rpf->entity.list_dev, &vsp1->entities);
}
if (vsp1->pdata->features & VSP1_HAS_SRU) {
vsp1->sru = vsp1_sru_create(vsp1);
if (IS_ERR(vsp1->sru)) {
ret = PTR_ERR(vsp1->sru);
goto done;
}
list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->uds_count; ++i) {
struct vsp1_uds *uds;
uds = vsp1_uds_create(vsp1, i);
if (IS_ERR(uds)) {
ret = PTR_ERR(uds);
goto done;
}
vsp1->uds[i] = uds;
list_add_tail(&uds->entity.list_dev, &vsp1->entities);
}
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
struct vsp1_rwpf *wpf;
wpf = vsp1_wpf_create(vsp1, i);
if (IS_ERR(wpf)) {
ret = PTR_ERR(wpf);
goto done;
}
vsp1->wpf[i] = wpf;
list_add_tail(&wpf->entity.list_dev, &vsp1->entities);
}
/* Create links. */
list_for_each_entry(entity, &vsp1->entities, list_dev) {
if (entity->type == VSP1_ENTITY_LIF ||
entity->type == VSP1_ENTITY_RPF)
continue;
ret = vsp1_create_links(vsp1, entity);
if (ret < 0)
goto done;
}
if (vsp1->pdata->features & VSP1_HAS_LIF) {
ret = media_entity_create_link(
&vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE,
&vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0);
if (ret < 0)
return ret;
}
/* Register all subdevs. */
list_for_each_entry(entity, &vsp1->entities, list_dev) {
ret = v4l2_device_register_subdev(&vsp1->v4l2_dev,
&entity->subdev);
if (ret < 0)
goto done;
}
ret = v4l2_device_register_subdev_nodes(&vsp1->v4l2_dev);
done:
if (ret < 0)
vsp1_destroy_entities(vsp1);
return ret;
}
static int vsp1_device_init(struct vsp1_device *vsp1)
{
unsigned int i;
u32 status;
/* Reset any channel that might be running. */
status = vsp1_read(vsp1, VI6_STATUS);
for (i = 0; i < vsp1->pdata->wpf_count; ++i) {
unsigned int timeout;
if (!(status & VI6_STATUS_SYS_ACT(i)))
continue;
vsp1_write(vsp1, VI6_SRESET, VI6_SRESET_SRTS(i));
for (timeout = 10; timeout > 0; --timeout) {
status = vsp1_read(vsp1, VI6_STATUS);
if (!(status & VI6_STATUS_SYS_ACT(i)))
break;
usleep_range(1000, 2000);
}
if (!timeout) {
dev_err(vsp1->dev, "failed to reset wpf.%u\n", i);
return -ETIMEDOUT;
}
}
vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) |
(8 << VI6_CLK_DCSWT_CSTRW_SHIFT));
for (i = 0; i < vsp1->pdata->rpf_count; ++i)
vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED);
for (i = 0; i < vsp1->pdata->uds_count; ++i)
vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_LUT_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_CLU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HST_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HSI_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_BRU_ROUTE, VI6_DPR_NODE_UNUSED);
vsp1_write(vsp1, VI6_DPR_HGO_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
vsp1_write(vsp1, VI6_DPR_HGT_SMPPT, (7 << VI6_DPR_SMPPT_TGW_SHIFT) |
(VI6_DPR_NODE_UNUSED << VI6_DPR_SMPPT_PT_SHIFT));
return 0;
}
/*
* vsp1_device_get - Acquire the VSP1 device
*
* Increment the VSP1 reference count and initialize the device if the first
* reference is taken.
*
* Return 0 on success or a negative error code otherwise.
*/
int vsp1_device_get(struct vsp1_device *vsp1)
{
int ret = 0;
mutex_lock(&vsp1->lock);
if (vsp1->ref_count > 0)
goto done;
ret = clk_prepare_enable(vsp1->clock);
if (ret < 0)
goto done;
ret = vsp1_device_init(vsp1);
if (ret < 0) {
clk_disable_unprepare(vsp1->clock);
goto done;
}
done:
if (!ret)
vsp1->ref_count++;
mutex_unlock(&vsp1->lock);
return ret;
}
/*
* vsp1_device_put - Release the VSP1 device
*
* Decrement the VSP1 reference count and cleanup the device if the last
* reference is released.
*/
void vsp1_device_put(struct vsp1_device *vsp1)
{
mutex_lock(&vsp1->lock);
if (--vsp1->ref_count == 0)
clk_disable_unprepare(vsp1->clock);
mutex_unlock(&vsp1->lock);
}
/* -----------------------------------------------------------------------------
* Power Management
*/
#ifdef CONFIG_PM_SLEEP
static int vsp1_pm_suspend(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
WARN_ON(mutex_is_locked(&vsp1->lock));
if (vsp1->ref_count == 0)
return 0;
clk_disable_unprepare(vsp1->clock);
return 0;
}
static int vsp1_pm_resume(struct device *dev)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
WARN_ON(mutex_is_locked(&vsp1->lock));
if (vsp1->ref_count)
return 0;
return clk_prepare_enable(vsp1->clock);
}
#endif
static const struct dev_pm_ops vsp1_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(vsp1_pm_suspend, vsp1_pm_resume)
};
/* -----------------------------------------------------------------------------
* Platform Driver
*/
static int vsp1_validate_platform_data(struct platform_device *pdev,
struct vsp1_platform_data *pdata)
{
if (pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
return -EINVAL;
}
if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) {
dev_err(&pdev->dev, "invalid number of RPF (%u)\n",
pdata->rpf_count);
return -EINVAL;
}
if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) {
dev_err(&pdev->dev, "invalid number of UDS (%u)\n",
pdata->uds_count);
return -EINVAL;
}
if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) {
dev_err(&pdev->dev, "invalid number of WPF (%u)\n",
pdata->wpf_count);
return -EINVAL;
}
return 0;
}
static struct vsp1_platform_data *
vsp1_get_platform_data(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct vsp1_platform_data *pdata;
if (!IS_ENABLED(CONFIG_OF) || np == NULL)
return pdev->dev.platform_data;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL)
return NULL;
if (of_property_read_bool(np, "renesas,has-lif"))
pdata->features |= VSP1_HAS_LIF;
if (of_property_read_bool(np, "renesas,has-lut"))
pdata->features |= VSP1_HAS_LUT;
if (of_property_read_bool(np, "renesas,has-sru"))
pdata->features |= VSP1_HAS_SRU;
of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count);
of_property_read_u32(np, "renesas,#uds", &pdata->uds_count);
of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count);
return pdata;
}
static int vsp1_probe(struct platform_device *pdev)
{
struct vsp1_device *vsp1;
struct resource *irq;
struct resource *io;
int ret;
vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
if (vsp1 == NULL)
return -ENOMEM;
vsp1->dev = &pdev->dev;
mutex_init(&vsp1->lock);
INIT_LIST_HEAD(&vsp1->entities);
vsp1->pdata = vsp1_get_platform_data(pdev);
if (vsp1->pdata == NULL)
return -ENODEV;
ret = vsp1_validate_platform_data(pdev, vsp1->pdata);
if (ret < 0)
return ret;
/* I/O, IRQ and clock resources */
io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vsp1->mmio = devm_ioremap_resource(&pdev->dev, io);
if (IS_ERR(vsp1->mmio))
return PTR_ERR(vsp1->mmio);
vsp1->clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(vsp1->clock)) {
dev_err(&pdev->dev, "failed to get clock\n");
return PTR_ERR(vsp1->clock);
}
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!irq) {
dev_err(&pdev->dev, "missing IRQ\n");
return -EINVAL;
}
ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
IRQF_SHARED, dev_name(&pdev->dev), vsp1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to request IRQ\n");
return ret;
}
/* Instanciate entities */
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create entities\n");
return ret;
}
platform_set_drvdata(pdev, vsp1);
return 0;
}
static int vsp1_remove(struct platform_device *pdev)
{
struct vsp1_device *vsp1 = platform_get_drvdata(pdev);
vsp1_destroy_entities(vsp1);
return 0;
}
static const struct of_device_id vsp1_of_match[] = {
{ .compatible = "renesas,vsp1" },
{ },
};
static struct platform_driver vsp1_platform_driver = {
.probe = vsp1_probe,
.remove = vsp1_remove,
.driver = {
.owner = THIS_MODULE,
.name = "vsp1",
.pm = &vsp1_pm_ops,
.of_match_table = vsp1_of_match,
},
};
module_platform_driver(vsp1_platform_driver);
MODULE_ALIAS("vsp1");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("Renesas VSP1 Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,233 @@
/*
* vsp1_entity.c -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_entity.h"
#include "vsp1_video.h"
bool vsp1_entity_is_streaming(struct vsp1_entity *entity)
{
bool streaming;
mutex_lock(&entity->lock);
streaming = entity->streaming;
mutex_unlock(&entity->lock);
return streaming;
}
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming)
{
int ret;
mutex_lock(&entity->lock);
entity->streaming = streaming;
mutex_unlock(&entity->lock);
if (!streaming)
return 0;
if (!entity->subdev.ctrl_handler)
return 0;
ret = v4l2_ctrl_handler_setup(entity->subdev.ctrl_handler);
if (ret < 0) {
mutex_lock(&entity->lock);
entity->streaming = false;
mutex_unlock(&entity->lock);
}
return ret;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_fh *fh,
unsigned int pad, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_format(fh, pad);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &entity->formats[pad];
default:
return NULL;
}
}
/*
* vsp1_entity_init_formats - Initialize formats on all pads
* @subdev: V4L2 subdevice
* @fh: V4L2 subdev file handle
*
* Initialize all pad formats with default values. If fh is not NULL, try
* formats are initialized on the file handle. Otherwise active formats are
* initialized on the device.
*/
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh)
{
struct v4l2_subdev_format format;
unsigned int pad;
for (pad = 0; pad < subdev->entity.num_pads - 1; ++pad) {
memset(&format, 0, sizeof(format));
format.pad = pad;
format.which = fh ? V4L2_SUBDEV_FORMAT_TRY
: V4L2_SUBDEV_FORMAT_ACTIVE;
v4l2_subdev_call(subdev, pad, set_fmt, fh, &format);
}
}
static int vsp1_entity_open(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh)
{
vsp1_entity_init_formats(subdev, fh);
return 0;
}
const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops = {
.open = vsp1_entity_open,
};
/* -----------------------------------------------------------------------------
* Media Operations
*/
static int vsp1_entity_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
struct vsp1_entity *source;
if (!(local->flags & MEDIA_PAD_FL_SOURCE))
return 0;
source = container_of(local->entity, struct vsp1_entity, subdev.entity);
if (!source->route)
return 0;
if (flags & MEDIA_LNK_FL_ENABLED) {
if (source->sink)
return -EBUSY;
source->sink = remote->entity;
source->sink_pad = remote->index;
} else {
source->sink = NULL;
source->sink_pad = 0;
}
return 0;
}
const struct media_entity_operations vsp1_media_ops = {
.link_setup = vsp1_entity_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
/* -----------------------------------------------------------------------------
* Initialization
*/
static const struct vsp1_route vsp1_routes[] = {
{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
{ VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
VI6_DPR_NODE_BRU_IN(2), VI6_DPR_NODE_BRU_IN(3), } },
{ VSP1_ENTITY_HSI, 0, VI6_DPR_HSI_ROUTE, { VI6_DPR_NODE_HSI, } },
{ VSP1_ENTITY_HST, 0, VI6_DPR_HST_ROUTE, { VI6_DPR_NODE_HST, } },
{ VSP1_ENTITY_LIF, 0, 0, { VI6_DPR_NODE_LIF, } },
{ VSP1_ENTITY_LUT, 0, VI6_DPR_LUT_ROUTE, { VI6_DPR_NODE_LUT, } },
{ VSP1_ENTITY_RPF, 0, VI6_DPR_RPF_ROUTE(0), { VI6_DPR_NODE_RPF(0), } },
{ VSP1_ENTITY_RPF, 1, VI6_DPR_RPF_ROUTE(1), { VI6_DPR_NODE_RPF(1), } },
{ VSP1_ENTITY_RPF, 2, VI6_DPR_RPF_ROUTE(2), { VI6_DPR_NODE_RPF(2), } },
{ VSP1_ENTITY_RPF, 3, VI6_DPR_RPF_ROUTE(3), { VI6_DPR_NODE_RPF(3), } },
{ VSP1_ENTITY_RPF, 4, VI6_DPR_RPF_ROUTE(4), { VI6_DPR_NODE_RPF(4), } },
{ VSP1_ENTITY_SRU, 0, VI6_DPR_SRU_ROUTE, { VI6_DPR_NODE_SRU, } },
{ VSP1_ENTITY_UDS, 0, VI6_DPR_UDS_ROUTE(0), { VI6_DPR_NODE_UDS(0), } },
{ VSP1_ENTITY_UDS, 1, VI6_DPR_UDS_ROUTE(1), { VI6_DPR_NODE_UDS(1), } },
{ VSP1_ENTITY_UDS, 2, VI6_DPR_UDS_ROUTE(2), { VI6_DPR_NODE_UDS(2), } },
{ VSP1_ENTITY_WPF, 0, 0, { VI6_DPR_NODE_WPF(0), } },
{ VSP1_ENTITY_WPF, 1, 0, { VI6_DPR_NODE_WPF(1), } },
{ VSP1_ENTITY_WPF, 2, 0, { VI6_DPR_NODE_WPF(2), } },
{ VSP1_ENTITY_WPF, 3, 0, { VI6_DPR_NODE_WPF(3), } },
};
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(vsp1_routes); ++i) {
if (vsp1_routes[i].type == entity->type &&
vsp1_routes[i].index == entity->index) {
entity->route = &vsp1_routes[i];
break;
}
}
if (i == ARRAY_SIZE(vsp1_routes))
return -EINVAL;
mutex_init(&entity->lock);
entity->vsp1 = vsp1;
entity->source_pad = num_pads - 1;
/* Allocate formats and pads. */
entity->formats = devm_kzalloc(vsp1->dev,
num_pads * sizeof(*entity->formats),
GFP_KERNEL);
if (entity->formats == NULL)
return -ENOMEM;
entity->pads = devm_kzalloc(vsp1->dev, num_pads * sizeof(*entity->pads),
GFP_KERNEL);
if (entity->pads == NULL)
return -ENOMEM;
/* Initialize pads. */
for (i = 0; i < num_pads - 1; ++i)
entity->pads[i].flags = MEDIA_PAD_FL_SINK;
entity->pads[num_pads - 1].flags = MEDIA_PAD_FL_SOURCE;
/* Initialize the media entity. */
return media_entity_init(&entity->subdev.entity, num_pads,
entity->pads, 0);
}
void vsp1_entity_destroy(struct vsp1_entity *entity)
{
if (entity->video)
vsp1_video_cleanup(entity->video);
if (entity->subdev.ctrl_handler)
v4l2_ctrl_handler_free(entity->subdev.ctrl_handler);
media_entity_cleanup(&entity->subdev.entity);
mutex_destroy(&entity->lock);
}

View file

@ -0,0 +1,102 @@
/*
* vsp1_entity.h -- R-Car VSP1 Base Entity
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_ENTITY_H__
#define __VSP1_ENTITY_H__
#include <linux/list.h>
#include <linux/mutex.h>
#include <media/v4l2-subdev.h>
struct vsp1_device;
struct vsp1_video;
enum vsp1_entity_type {
VSP1_ENTITY_BRU,
VSP1_ENTITY_HSI,
VSP1_ENTITY_HST,
VSP1_ENTITY_LIF,
VSP1_ENTITY_LUT,
VSP1_ENTITY_RPF,
VSP1_ENTITY_SRU,
VSP1_ENTITY_UDS,
VSP1_ENTITY_WPF,
};
/*
* struct vsp1_route - Entity routing configuration
* @type: Entity type this routing entry is associated with
* @index: Entity index this routing entry is associated with
* @reg: Output routing configuration register
* @inputs: Target node value for each input
*
* Each $vsp1_route entry describes routing configuration for the entity
* specified by the entry's @type and @index. @reg indicates the register that
* holds output routing configuration for the entity, and the @inputs array
* store the target node value for each input of the entity.
*/
struct vsp1_route {
enum vsp1_entity_type type;
unsigned int index;
unsigned int reg;
unsigned int inputs[4];
};
struct vsp1_entity {
struct vsp1_device *vsp1;
enum vsp1_entity_type type;
unsigned int index;
const struct vsp1_route *route;
struct list_head list_dev;
struct list_head list_pipe;
struct media_pad *pads;
unsigned int source_pad;
struct media_entity *sink;
unsigned int sink_pad;
struct v4l2_subdev subdev;
struct v4l2_mbus_framefmt *formats;
struct vsp1_video *video;
struct mutex lock; /* Protects the streaming field */
bool streaming;
};
static inline struct vsp1_entity *to_vsp1_entity(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_entity, subdev);
}
int vsp1_entity_init(struct vsp1_device *vsp1, struct vsp1_entity *entity,
unsigned int num_pads);
void vsp1_entity_destroy(struct vsp1_entity *entity);
extern const struct v4l2_subdev_internal_ops vsp1_subdev_internal_ops;
extern const struct media_entity_operations vsp1_media_ops;
struct v4l2_mbus_framefmt *
vsp1_entity_get_pad_format(struct vsp1_entity *entity,
struct v4l2_subdev_fh *fh,
unsigned int pad, u32 which);
void vsp1_entity_init_formats(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh);
bool vsp1_entity_is_streaming(struct vsp1_entity *entity);
int vsp1_entity_set_streaming(struct vsp1_entity *entity, bool streaming);
#endif /* __VSP1_ENTITY_H__ */

View file

@ -0,0 +1,219 @@
/*
* vsp1_hsit.c -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_hsit.h"
#define HSIT_MIN_SIZE 4U
#define HSIT_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg)
{
return vsp1_read(hsit->entity.vsp1, reg);
}
static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data)
{
vsp1_write(hsit->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int hsit_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
if (!enable)
return 0;
if (hsit->inverse)
vsp1_hsit_write(hsit, VI6_HSI_CTRL, VI6_HSI_CTRL_EN);
else
vsp1_hsit_write(hsit, VI6_HST_CTRL, VI6_HST_CTRL_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int hsit_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
if (code->index > 0)
return -EINVAL;
if ((code->pad == HSIT_PAD_SINK && !hsit->inverse) |
(code->pad == HSIT_PAD_SOURCE && hsit->inverse))
code->code = V4L2_MBUS_FMT_ARGB8888_1X32;
else
code->code = V4L2_MBUS_FMT_AHSV8888_1X32;
return 0;
}
static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, fse->pad);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == HSIT_PAD_SINK) {
fse->min_width = HSIT_MIN_SIZE;
fse->max_width = HSIT_MAX_SIZE;
fse->min_height = HSIT_MIN_SIZE;
fse->max_height = HSIT_MAX_SIZE;
} else {
/* The size on the source pad are fixed and always identical to
* the size on the sink pad.
*/
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
static int hsit_get_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
fmt->format = *vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static int hsit_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_hsit *hsit = to_hsit(subdev);
struct v4l2_mbus_framefmt *format;
format = vsp1_entity_get_pad_format(&hsit->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == HSIT_PAD_SOURCE) {
/* The HST and HSI output format code and resolution can't be
* modified.
*/
fmt->format = *format;
return 0;
}
format->code = hsit->inverse ? V4L2_MBUS_FMT_AHSV8888_1X32
: V4L2_MBUS_FMT_ARGB8888_1X32;
format->width = clamp_t(unsigned int, fmt->format.width,
HSIT_MIN_SIZE, HSIT_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
HSIT_MIN_SIZE, HSIT_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&hsit->entity, fh, HSIT_PAD_SOURCE,
fmt->which);
*format = fmt->format;
format->code = hsit->inverse ? V4L2_MBUS_FMT_ARGB8888_1X32
: V4L2_MBUS_FMT_AHSV8888_1X32;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops hsit_video_ops = {
.s_stream = hsit_s_stream,
};
static struct v4l2_subdev_pad_ops hsit_pad_ops = {
.enum_mbus_code = hsit_enum_mbus_code,
.enum_frame_size = hsit_enum_frame_size,
.get_fmt = hsit_get_format,
.set_fmt = hsit_set_format,
};
static struct v4l2_subdev_ops hsit_ops = {
.video = &hsit_video_ops,
.pad = &hsit_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse)
{
struct v4l2_subdev *subdev;
struct vsp1_hsit *hsit;
int ret;
hsit = devm_kzalloc(vsp1->dev, sizeof(*hsit), GFP_KERNEL);
if (hsit == NULL)
return ERR_PTR(-ENOMEM);
hsit->inverse = inverse;
if (inverse)
hsit->entity.type = VSP1_ENTITY_HSI;
else
hsit->entity.type = VSP1_ENTITY_HST;
ret = vsp1_entity_init(vsp1, &hsit->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &hsit->entity.subdev;
v4l2_subdev_init(subdev, &hsit_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s %s",
dev_name(vsp1->dev), inverse ? "hsi" : "hst");
v4l2_set_subdevdata(subdev, hsit);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return hsit;
}

View file

@ -0,0 +1,38 @@
/*
* vsp1_hsit.h -- R-Car VSP1 Hue Saturation value (Inverse) Transform
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_HSIT_H__
#define __VSP1_HSIT_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define HSIT_PAD_SINK 0
#define HSIT_PAD_SOURCE 1
struct vsp1_hsit {
struct vsp1_entity entity;
bool inverse;
};
static inline struct vsp1_hsit *to_hsit(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_hsit, entity.subdev);
}
struct vsp1_hsit *vsp1_hsit_create(struct vsp1_device *vsp1, bool inverse);
#endif /* __VSP1_HSIT_H__ */

View file

@ -0,0 +1,237 @@
/*
* vsp1_lif.c -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_lif.h"
#define LIF_MIN_SIZE 2U
#define LIF_MAX_SIZE 2048U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_lif_read(struct vsp1_lif *lif, u32 reg)
{
return vsp1_read(lif->entity.vsp1, reg);
}
static inline void vsp1_lif_write(struct vsp1_lif *lif, u32 reg, u32 data)
{
vsp1_write(lif->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int lif_s_stream(struct v4l2_subdev *subdev, int enable)
{
const struct v4l2_mbus_framefmt *format;
struct vsp1_lif *lif = to_lif(subdev);
unsigned int hbth = 1300;
unsigned int obth = 400;
unsigned int lbth = 200;
if (!enable) {
vsp1_lif_write(lif, VI6_LIF_CTRL, 0);
return 0;
}
format = &lif->entity.formats[LIF_PAD_SOURCE];
obth = min(obth, (format->width + 1) / 2 * format->height - 4);
vsp1_lif_write(lif, VI6_LIF_CSBTH,
(hbth << VI6_LIF_CSBTH_HBTH_SHIFT) |
(lbth << VI6_LIF_CSBTH_LBTH_SHIFT));
vsp1_lif_write(lif, VI6_LIF_CTRL,
(obth << VI6_LIF_CTRL_OBTH_SHIFT) |
(format->code == 0 ? VI6_LIF_CTRL_CFMT : 0) |
VI6_LIF_CTRL_REQSEL | VI6_LIF_CTRL_LIF_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int lif_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->pad == LIF_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
struct v4l2_mbus_framefmt *format;
/* The LIF can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int lif_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, LIF_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == LIF_PAD_SINK) {
fse->min_width = LIF_MIN_SIZE;
fse->max_width = LIF_MAX_SIZE;
fse->min_height = LIF_MIN_SIZE;
fse->max_height = LIF_MAX_SIZE;
} else {
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
static int lif_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
fmt->format = *vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static int lif_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lif *lif = to_lif(subdev);
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lif->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == LIF_PAD_SOURCE) {
/* The LIF source format is always identical to its sink
* format.
*/
fmt->format = *format;
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
LIF_MIN_SIZE, LIF_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
LIF_MIN_SIZE, LIF_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&lif->entity, fh, LIF_PAD_SOURCE,
fmt->which);
*format = fmt->format;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops lif_video_ops = {
.s_stream = lif_s_stream,
};
static struct v4l2_subdev_pad_ops lif_pad_ops = {
.enum_mbus_code = lif_enum_mbus_code,
.enum_frame_size = lif_enum_frame_size,
.get_fmt = lif_get_format,
.set_fmt = lif_set_format,
};
static struct v4l2_subdev_ops lif_ops = {
.video = &lif_video_ops,
.pad = &lif_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_lif *lif;
int ret;
lif = devm_kzalloc(vsp1->dev, sizeof(*lif), GFP_KERNEL);
if (lif == NULL)
return ERR_PTR(-ENOMEM);
lif->entity.type = VSP1_ENTITY_LIF;
ret = vsp1_entity_init(vsp1, &lif->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &lif->entity.subdev;
v4l2_subdev_init(subdev, &lif_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lif",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, lif);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return lif;
}

View file

@ -0,0 +1,37 @@
/*
* vsp1_lif.h -- R-Car VSP1 LCD Controller Interface
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_LIF_H__
#define __VSP1_LIF_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define LIF_PAD_SINK 0
#define LIF_PAD_SOURCE 1
struct vsp1_lif {
struct vsp1_entity entity;
};
static inline struct vsp1_lif *to_lif(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_lif, entity.subdev);
}
struct vsp1_lif *vsp1_lif_create(struct vsp1_device *vsp1);
#endif /* __VSP1_LIF_H__ */

View file

@ -0,0 +1,251 @@
/*
* vsp1_lut.c -- R-Car VSP1 Look-Up Table
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <linux/vsp1.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_lut.h"
#define LUT_MIN_SIZE 4U
#define LUT_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_lut_read(struct vsp1_lut *lut, u32 reg)
{
return vsp1_read(lut->entity.vsp1, reg);
}
static inline void vsp1_lut_write(struct vsp1_lut *lut, u32 reg, u32 data)
{
vsp1_write(lut->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static void lut_configure(struct vsp1_lut *lut, struct vsp1_lut_config *config)
{
memcpy_toio(lut->entity.vsp1->mmio + VI6_LUT_TABLE, config->lut,
sizeof(config->lut));
}
static long lut_ioctl(struct v4l2_subdev *subdev, unsigned int cmd, void *arg)
{
struct vsp1_lut *lut = to_lut(subdev);
switch (cmd) {
case VIDIOC_VSP1_LUT_CONFIG:
lut_configure(lut, arg);
return 0;
default:
return -ENOIOCTLCMD;
}
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Video Operations
*/
static int lut_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_lut *lut = to_lut(subdev);
if (!enable)
return 0;
vsp1_lut_write(lut, VI6_LUT_CTRL, VI6_LUT_CTRL_EN);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int lut_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AHSV8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
if (code->pad == LUT_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
/* The LUT can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, LUT_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int lut_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, fse->pad);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == LUT_PAD_SINK) {
fse->min_width = LUT_MIN_SIZE;
fse->max_width = LUT_MAX_SIZE;
fse->min_height = LUT_MIN_SIZE;
fse->max_height = LUT_MAX_SIZE;
} else {
/* The size on the source pad are fixed and always identical to
* the size on the sink pad.
*/
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
static int lut_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lut *lut = to_lut(subdev);
fmt->format = *vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static int lut_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_lut *lut = to_lut(subdev);
struct v4l2_mbus_framefmt *format;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AHSV8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&lut->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == LUT_PAD_SOURCE) {
/* The LUT output format can't be modified. */
fmt->format = *format;
return 0;
}
format->width = clamp_t(unsigned int, fmt->format.width,
LUT_MIN_SIZE, LUT_MAX_SIZE);
format->height = clamp_t(unsigned int, fmt->format.height,
LUT_MIN_SIZE, LUT_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
fmt->format = *format;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&lut->entity, fh, LUT_PAD_SOURCE,
fmt->which);
*format = fmt->format;
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_core_ops lut_core_ops = {
.ioctl = lut_ioctl,
};
static struct v4l2_subdev_video_ops lut_video_ops = {
.s_stream = lut_s_stream,
};
static struct v4l2_subdev_pad_ops lut_pad_ops = {
.enum_mbus_code = lut_enum_mbus_code,
.enum_frame_size = lut_enum_frame_size,
.get_fmt = lut_get_format,
.set_fmt = lut_set_format,
};
static struct v4l2_subdev_ops lut_ops = {
.core = &lut_core_ops,
.video = &lut_video_ops,
.pad = &lut_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_lut *lut;
int ret;
lut = devm_kzalloc(vsp1->dev, sizeof(*lut), GFP_KERNEL);
if (lut == NULL)
return ERR_PTR(-ENOMEM);
lut->entity.type = VSP1_ENTITY_LUT;
ret = vsp1_entity_init(vsp1, &lut->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &lut->entity.subdev;
v4l2_subdev_init(subdev, &lut_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s lut",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, lut);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return lut;
}

View file

@ -0,0 +1,38 @@
/*
* vsp1_lut.h -- R-Car VSP1 Look-Up Table
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_LUT_H__
#define __VSP1_LUT_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define LUT_PAD_SINK 0
#define LUT_PAD_SOURCE 1
struct vsp1_lut {
struct vsp1_entity entity;
u32 lut[256];
};
static inline struct vsp1_lut *to_lut(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_lut, entity.subdev);
}
struct vsp1_lut *vsp1_lut_create(struct vsp1_device *vsp1);
#endif /* __VSP1_LUT_H__ */

View file

@ -0,0 +1,697 @@
/*
* vsp1_regs.h -- R-Car VSP1 Registers Definitions
*
* Copyright (C) 2013 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
#ifndef __VSP1_REGS_H__
#define __VSP1_REGS_H__
/* -----------------------------------------------------------------------------
* General Control Registers
*/
#define VI6_CMD(n) (0x0000 + (n) * 4)
#define VI6_CMD_STRCMD (1 << 0)
#define VI6_CLK_DCSWT 0x0018
#define VI6_CLK_DCSWT_CSTPW_MASK (0xff << 8)
#define VI6_CLK_DCSWT_CSTPW_SHIFT 8
#define VI6_CLK_DCSWT_CSTRW_MASK (0xff << 0)
#define VI6_CLK_DCSWT_CSTRW_SHIFT 0
#define VI6_SRESET 0x0028
#define VI6_SRESET_SRTS(n) (1 << (n))
#define VI6_STATUS 0x0038
#define VI6_STATUS_SYS_ACT(n) (1 << ((n) + 8))
#define VI6_WPF_IRQ_ENB(n) (0x0048 + (n) * 12)
#define VI6_WFP_IRQ_ENB_DFEE (1 << 1)
#define VI6_WFP_IRQ_ENB_FREE (1 << 0)
#define VI6_WPF_IRQ_STA(n) (0x004c + (n) * 12)
#define VI6_WFP_IRQ_STA_DFE (1 << 1)
#define VI6_WFP_IRQ_STA_FRE (1 << 0)
#define VI6_DISP_IRQ_ENB 0x0078
#define VI6_DISP_IRQ_ENB_DSTE (1 << 8)
#define VI6_DISP_IRQ_ENB_MAEE (1 << 5)
#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4))
#define VI6_DISP_IRQ_STA 0x007c
#define VI6_DISP_IRQ_STA_DSE (1 << 8)
#define VI6_DISP_IRQ_STA_MAE (1 << 5)
#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4))
#define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4)
#define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0)
/* -----------------------------------------------------------------------------
* Display List Control Registers
*/
#define VI6_DL_CTRL 0x0100
#define VI6_DL_CTRL_AR_WAIT_MASK (0xffff << 16)
#define VI6_DL_CTRL_AR_WAIT_SHIFT 16
#define VI6_DL_CTRL_DC2 (1 << 12)
#define VI6_DL_CTRL_DC1 (1 << 8)
#define VI6_DL_CTRL_DC0 (1 << 4)
#define VI6_DL_CTRL_CFM0 (1 << 2)
#define VI6_DL_CTRL_NH0 (1 << 1)
#define VI6_DL_CTRL_DLE (1 << 0)
#define VI6_DL_HDR_ADDR(n) (0x0104 + (n) * 4)
#define VI6_DL_SWAP 0x0114
#define VI6_DL_SWAP_LWS (1 << 2)
#define VI6_DL_SWAP_WDS (1 << 1)
#define VI6_DL_SWAP_BTS (1 << 0)
#define VI6_DL_EXT_CTRL 0x011c
#define VI6_DL_EXT_CTRL_NWE (1 << 16)
#define VI6_DL_EXT_CTRL_POLINT_MASK (0x3f << 8)
#define VI6_DL_EXT_CTRL_POLINT_SHIFT 8
#define VI6_DL_EXT_CTRL_DLPRI (1 << 5)
#define VI6_DL_EXT_CTRL_EXPRI (1 << 4)
#define VI6_DL_EXT_CTRL_EXT (1 << 0)
#define VI6_DL_BODY_SIZE 0x0120
#define VI6_DL_BODY_SIZE_UPD (1 << 24)
#define VI6_DL_BODY_SIZE_BS_MASK (0x1ffff << 0)
#define VI6_DL_BODY_SIZE_BS_SHIFT 0
/* -----------------------------------------------------------------------------
* RPF Control Registers
*/
#define VI6_RPF_OFFSET 0x100
#define VI6_RPF_SRC_BSIZE 0x0300
#define VI6_RPF_SRC_BSIZE_BHSIZE_MASK (0x1fff << 16)
#define VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT 16
#define VI6_RPF_SRC_BSIZE_BVSIZE_MASK (0x1fff << 0)
#define VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT 0
#define VI6_RPF_SRC_ESIZE 0x0304
#define VI6_RPF_SRC_ESIZE_EHSIZE_MASK (0x1fff << 16)
#define VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT 16
#define VI6_RPF_SRC_ESIZE_EVSIZE_MASK (0x1fff << 0)
#define VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT 0
#define VI6_RPF_INFMT 0x0308
#define VI6_RPF_INFMT_VIR (1 << 28)
#define VI6_RPF_INFMT_CIPM (1 << 16)
#define VI6_RPF_INFMT_SPYCS (1 << 15)
#define VI6_RPF_INFMT_SPUVS (1 << 14)
#define VI6_RPF_INFMT_CEXT_ZERO (0 << 12)
#define VI6_RPF_INFMT_CEXT_EXT (1 << 12)
#define VI6_RPF_INFMT_CEXT_ONE (2 << 12)
#define VI6_RPF_INFMT_CEXT_MASK (3 << 12)
#define VI6_RPF_INFMT_RDTM_BT601 (0 << 9)
#define VI6_RPF_INFMT_RDTM_BT601_EXT (1 << 9)
#define VI6_RPF_INFMT_RDTM_BT709 (2 << 9)
#define VI6_RPF_INFMT_RDTM_BT709_EXT (3 << 9)
#define VI6_RPF_INFMT_RDTM_MASK (7 << 9)
#define VI6_RPF_INFMT_CSC (1 << 8)
#define VI6_RPF_INFMT_RDFMT_MASK (0x7f << 0)
#define VI6_RPF_INFMT_RDFMT_SHIFT 0
#define VI6_RPF_DSWAP 0x030c
#define VI6_RPF_DSWAP_A_LLS (1 << 11)
#define VI6_RPF_DSWAP_A_LWS (1 << 10)
#define VI6_RPF_DSWAP_A_WDS (1 << 9)
#define VI6_RPF_DSWAP_A_BTS (1 << 8)
#define VI6_RPF_DSWAP_P_LLS (1 << 3)
#define VI6_RPF_DSWAP_P_LWS (1 << 2)
#define VI6_RPF_DSWAP_P_WDS (1 << 1)
#define VI6_RPF_DSWAP_P_BTS (1 << 0)
#define VI6_RPF_LOC 0x0310
#define VI6_RPF_LOC_HCOORD_MASK (0x1fff << 16)
#define VI6_RPF_LOC_HCOORD_SHIFT 16
#define VI6_RPF_LOC_VCOORD_MASK (0x1fff << 0)
#define VI6_RPF_LOC_VCOORD_SHIFT 0
#define VI6_RPF_ALPH_SEL 0x0314
#define VI6_RPF_ALPH_SEL_ASEL_PACKED (0 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_8B_PLANE (1 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_SELECT (2 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_1B_PLANE (3 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_FIXED (4 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_MASK (7 << 28)
#define VI6_RPF_ALPH_SEL_ASEL_SHIFT 28
#define VI6_RPF_ALPH_SEL_IROP_MASK (0xf << 24)
#define VI6_RPF_ALPH_SEL_IROP_SHIFT 24
#define VI6_RPF_ALPH_SEL_BSEL (1 << 23)
#define VI6_RPF_ALPH_SEL_AEXT_ZERO (0 << 18)
#define VI6_RPF_ALPH_SEL_AEXT_EXT (1 << 18)
#define VI6_RPF_ALPH_SEL_AEXT_ONE (2 << 18)
#define VI6_RPF_ALPH_SEL_AEXT_MASK (3 << 18)
#define VI6_RPF_ALPH_SEL_ALPHA0_MASK (0xff << 8)
#define VI6_RPF_ALPH_SEL_ALPHA0_SHIFT 8
#define VI6_RPF_ALPH_SEL_ALPHA1_MASK (0xff << 0)
#define VI6_RPF_ALPH_SEL_ALPHA1_SHIFT 0
#define VI6_RPF_VRTCOL_SET 0x0318
#define VI6_RPF_VRTCOL_SET_LAYA_MASK (0xff << 24)
#define VI6_RPF_VRTCOL_SET_LAYA_SHIFT 24
#define VI6_RPF_VRTCOL_SET_LAYR_MASK (0xff << 16)
#define VI6_RPF_VRTCOL_SET_LAYR_SHIFT 16
#define VI6_RPF_VRTCOL_SET_LAYG_MASK (0xff << 8)
#define VI6_RPF_VRTCOL_SET_LAYG_SHIFT 8
#define VI6_RPF_VRTCOL_SET_LAYB_MASK (0xff << 0)
#define VI6_RPF_VRTCOL_SET_LAYB_SHIFT 0
#define VI6_RPF_MSK_CTRL 0x031c
#define VI6_RPF_MSK_CTRL_MSK_EN (1 << 24)
#define VI6_RPF_MSK_CTRL_MGR_MASK (0xff << 16)
#define VI6_RPF_MSK_CTRL_MGR_SHIFT 16
#define VI6_RPF_MSK_CTRL_MGG_MASK (0xff << 8)
#define VI6_RPF_MSK_CTRL_MGG_SHIFT 8
#define VI6_RPF_MSK_CTRL_MGB_MASK (0xff << 0)
#define VI6_RPF_MSK_CTRL_MGB_SHIFT 0
#define VI6_RPF_MSK_SET0 0x0320
#define VI6_RPF_MSK_SET1 0x0324
#define VI6_RPF_MSK_SET_MSA_MASK (0xff << 24)
#define VI6_RPF_MSK_SET_MSA_SHIFT 24
#define VI6_RPF_MSK_SET_MSR_MASK (0xff << 16)
#define VI6_RPF_MSK_SET_MSR_SHIFT 16
#define VI6_RPF_MSK_SET_MSG_MASK (0xff << 8)
#define VI6_RPF_MSK_SET_MSG_SHIFT 8
#define VI6_RPF_MSK_SET_MSB_MASK (0xff << 0)
#define VI6_RPF_MSK_SET_MSB_SHIFT 0
#define VI6_RPF_CKEY_CTRL 0x0328
#define VI6_RPF_CKEY_CTRL_CV (1 << 4)
#define VI6_RPF_CKEY_CTRL_SAPE1 (1 << 1)
#define VI6_RPF_CKEY_CTRL_SAPE0 (1 << 0)
#define VI6_RPF_CKEY_SET0 0x032c
#define VI6_RPF_CKEY_SET1 0x0330
#define VI6_RPF_CKEY_SET_AP_MASK (0xff << 24)
#define VI6_RPF_CKEY_SET_AP_SHIFT 24
#define VI6_RPF_CKEY_SET_R_MASK (0xff << 16)
#define VI6_RPF_CKEY_SET_R_SHIFT 16
#define VI6_RPF_CKEY_SET_GY_MASK (0xff << 8)
#define VI6_RPF_CKEY_SET_GY_SHIFT 8
#define VI6_RPF_CKEY_SET_B_MASK (0xff << 0)
#define VI6_RPF_CKEY_SET_B_SHIFT 0
#define VI6_RPF_SRCM_PSTRIDE 0x0334
#define VI6_RPF_SRCM_PSTRIDE_Y_SHIFT 16
#define VI6_RPF_SRCM_PSTRIDE_C_SHIFT 0
#define VI6_RPF_SRCM_ASTRIDE 0x0338
#define VI6_RPF_SRCM_PSTRIDE_A_SHIFT 0
#define VI6_RPF_SRCM_ADDR_Y 0x033c
#define VI6_RPF_SRCM_ADDR_C0 0x0340
#define VI6_RPF_SRCM_ADDR_C1 0x0344
#define VI6_RPF_SRCM_ADDR_AI 0x0348
/* -----------------------------------------------------------------------------
* WPF Control Registers
*/
#define VI6_WPF_OFFSET 0x100
#define VI6_WPF_SRCRPF 0x1000
#define VI6_WPF_SRCRPF_VIRACT_DIS (0 << 28)
#define VI6_WPF_SRCRPF_VIRACT_SUB (1 << 28)
#define VI6_WPF_SRCRPF_VIRACT_MST (2 << 28)
#define VI6_WPF_SRCRPF_VIRACT_MASK (3 << 28)
#define VI6_WPF_SRCRPF_RPF_ACT_DIS(n) (0 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_SUB(n) (1 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_MST(n) (2 << ((n) * 2))
#define VI6_WPF_SRCRPF_RPF_ACT_MASK(n) (3 << ((n) * 2))
#define VI6_WPF_HSZCLIP 0x1004
#define VI6_WPF_VSZCLIP 0x1008
#define VI6_WPF_SZCLIP_EN (1 << 28)
#define VI6_WPF_SZCLIP_OFST_MASK (0xff << 16)
#define VI6_WPF_SZCLIP_OFST_SHIFT 16
#define VI6_WPF_SZCLIP_SIZE_MASK (0x1fff << 0)
#define VI6_WPF_SZCLIP_SIZE_SHIFT 0
#define VI6_WPF_OUTFMT 0x100c
#define VI6_WPF_OUTFMT_PDV_MASK (0xff << 24)
#define VI6_WPF_OUTFMT_PDV_SHIFT 24
#define VI6_WPF_OUTFMT_PXA (1 << 23)
#define VI6_WPF_OUTFMT_FLP (1 << 16)
#define VI6_WPF_OUTFMT_SPYCS (1 << 15)
#define VI6_WPF_OUTFMT_SPUVS (1 << 14)
#define VI6_WPF_OUTFMT_DITH_DIS (0 << 12)
#define VI6_WPF_OUTFMT_DITH_EN (3 << 12)
#define VI6_WPF_OUTFMT_DITH_MASK (3 << 12)
#define VI6_WPF_OUTFMT_WRTM_BT601 (0 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT601_EXT (1 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT709 (2 << 9)
#define VI6_WPF_OUTFMT_WRTM_BT709_EXT (3 << 9)
#define VI6_WPF_OUTFMT_WRTM_MASK (7 << 9)
#define VI6_WPF_OUTFMT_CSC (1 << 8)
#define VI6_WPF_OUTFMT_WRFMT_MASK (0x7f << 0)
#define VI6_WPF_OUTFMT_WRFMT_SHIFT 0
#define VI6_WPF_DSWAP 0x1010
#define VI6_WPF_DSWAP_P_LLS (1 << 3)
#define VI6_WPF_DSWAP_P_LWS (1 << 2)
#define VI6_WPF_DSWAP_P_WDS (1 << 1)
#define VI6_WPF_DSWAP_P_BTS (1 << 0)
#define VI6_WPF_RNDCTRL 0x1014
#define VI6_WPF_RNDCTRL_CBRM (1 << 28)
#define VI6_WPF_RNDCTRL_ABRM_TRUNC (0 << 24)
#define VI6_WPF_RNDCTRL_ABRM_ROUND (1 << 24)
#define VI6_WPF_RNDCTRL_ABRM_THRESH (2 << 24)
#define VI6_WPF_RNDCTRL_ABRM_MASK (3 << 24)
#define VI6_WPF_RNDCTRL_ATHRESH_MASK (0xff << 16)
#define VI6_WPF_RNDCTRL_ATHRESH_SHIFT 16
#define VI6_WPF_RNDCTRL_CLMD_FULL (0 << 12)
#define VI6_WPF_RNDCTRL_CLMD_CLIP (1 << 12)
#define VI6_WPF_RNDCTRL_CLMD_EXT (2 << 12)
#define VI6_WPF_RNDCTRL_CLMD_MASK (3 << 12)
#define VI6_WPF_DSTM_STRIDE_Y 0x101c
#define VI6_WPF_DSTM_STRIDE_C 0x1020
#define VI6_WPF_DSTM_ADDR_Y 0x1024
#define VI6_WPF_DSTM_ADDR_C0 0x1028
#define VI6_WPF_DSTM_ADDR_C1 0x102c
#define VI6_WPF_WRBCK_CTRL 0x1034
#define VI6_WPF_WRBCK_CTRL_WBMD (1 << 0)
/* -----------------------------------------------------------------------------
* DPR Control Registers
*/
#define VI6_DPR_RPF_ROUTE(n) (0x2000 + (n) * 4)
#define VI6_DPR_WPF_FPORCH(n) (0x2014 + (n) * 4)
#define VI6_DPR_WPF_FPORCH_FP_WPFN (5 << 8)
#define VI6_DPR_SRU_ROUTE 0x2024
#define VI6_DPR_UDS_ROUTE(n) (0x2028 + (n) * 4)
#define VI6_DPR_LUT_ROUTE 0x203c
#define VI6_DPR_CLU_ROUTE 0x2040
#define VI6_DPR_HST_ROUTE 0x2044
#define VI6_DPR_HSI_ROUTE 0x2048
#define VI6_DPR_BRU_ROUTE 0x204c
#define VI6_DPR_ROUTE_FXA_MASK (0xff << 8)
#define VI6_DPR_ROUTE_FXA_SHIFT 16
#define VI6_DPR_ROUTE_FP_MASK (0xff << 8)
#define VI6_DPR_ROUTE_FP_SHIFT 8
#define VI6_DPR_ROUTE_RT_MASK (0x3f << 0)
#define VI6_DPR_ROUTE_RT_SHIFT 0
#define VI6_DPR_HGO_SMPPT 0x2050
#define VI6_DPR_HGT_SMPPT 0x2054
#define VI6_DPR_SMPPT_TGW_MASK (7 << 8)
#define VI6_DPR_SMPPT_TGW_SHIFT 8
#define VI6_DPR_SMPPT_PT_MASK (0x3f << 0)
#define VI6_DPR_SMPPT_PT_SHIFT 0
#define VI6_DPR_NODE_RPF(n) (n)
#define VI6_DPR_NODE_SRU 16
#define VI6_DPR_NODE_UDS(n) (17 + (n))
#define VI6_DPR_NODE_LUT 22
#define VI6_DPR_NODE_BRU_IN(n) (23 + (n))
#define VI6_DPR_NODE_BRU_OUT 27
#define VI6_DPR_NODE_CLU 29
#define VI6_DPR_NODE_HST 30
#define VI6_DPR_NODE_HSI 31
#define VI6_DPR_NODE_LIF 55
#define VI6_DPR_NODE_WPF(n) (56 + (n))
#define VI6_DPR_NODE_UNUSED 63
/* -----------------------------------------------------------------------------
* SRU Control Registers
*/
#define VI6_SRU_CTRL0 0x2200
#define VI6_SRU_CTRL0_PARAM0_MASK (0x1ff << 16)
#define VI6_SRU_CTRL0_PARAM0_SHIFT 16
#define VI6_SRU_CTRL0_PARAM1_MASK (0x1f << 8)
#define VI6_SRU_CTRL0_PARAM1_SHIFT 8
#define VI6_SRU_CTRL0_MODE_UPSCALE (4 << 4)
#define VI6_SRU_CTRL0_PARAM2 (1 << 3)
#define VI6_SRU_CTRL0_PARAM3 (1 << 2)
#define VI6_SRU_CTRL0_PARAM4 (1 << 1)
#define VI6_SRU_CTRL0_EN (1 << 0)
#define VI6_SRU_CTRL1 0x2204
#define VI6_SRU_CTRL1_PARAM5 0x7ff
#define VI6_SRU_CTRL2 0x2208
#define VI6_SRU_CTRL2_PARAM6_SHIFT 16
#define VI6_SRU_CTRL2_PARAM7_SHIFT 8
#define VI6_SRU_CTRL2_PARAM8_SHIFT 0
/* -----------------------------------------------------------------------------
* UDS Control Registers
*/
#define VI6_UDS_OFFSET 0x100
#define VI6_UDS_CTRL 0x2300
#define VI6_UDS_CTRL_AMD (1 << 30)
#define VI6_UDS_CTRL_FMD (1 << 29)
#define VI6_UDS_CTRL_BLADV (1 << 28)
#define VI6_UDS_CTRL_AON (1 << 25)
#define VI6_UDS_CTRL_ATHON (1 << 24)
#define VI6_UDS_CTRL_BC (1 << 20)
#define VI6_UDS_CTRL_NE_A (1 << 19)
#define VI6_UDS_CTRL_NE_RCR (1 << 18)
#define VI6_UDS_CTRL_NE_GY (1 << 17)
#define VI6_UDS_CTRL_NE_BCB (1 << 16)
#define VI6_UDS_CTRL_TDIPC (1 << 1)
#define VI6_UDS_SCALE 0x2304
#define VI6_UDS_SCALE_HMANT_MASK (0xf << 28)
#define VI6_UDS_SCALE_HMANT_SHIFT 28
#define VI6_UDS_SCALE_HFRAC_MASK (0xfff << 16)
#define VI6_UDS_SCALE_HFRAC_SHIFT 16
#define VI6_UDS_SCALE_VMANT_MASK (0xf << 12)
#define VI6_UDS_SCALE_VMANT_SHIFT 12
#define VI6_UDS_SCALE_VFRAC_MASK (0xfff << 0)
#define VI6_UDS_SCALE_VFRAC_SHIFT 0
#define VI6_UDS_ALPTH 0x2308
#define VI6_UDS_ALPTH_TH1_MASK (0xff << 8)
#define VI6_UDS_ALPTH_TH1_SHIFT 8
#define VI6_UDS_ALPTH_TH0_MASK (0xff << 0)
#define VI6_UDS_ALPTH_TH0_SHIFT 0
#define VI6_UDS_ALPVAL 0x230c
#define VI6_UDS_ALPVAL_VAL2_MASK (0xff << 16)
#define VI6_UDS_ALPVAL_VAL2_SHIFT 16
#define VI6_UDS_ALPVAL_VAL1_MASK (0xff << 8)
#define VI6_UDS_ALPVAL_VAL1_SHIFT 8
#define VI6_UDS_ALPVAL_VAL0_MASK (0xff << 0)
#define VI6_UDS_ALPVAL_VAL0_SHIFT 0
#define VI6_UDS_PASS_BWIDTH 0x2310
#define VI6_UDS_PASS_BWIDTH_H_MASK (0x7f << 16)
#define VI6_UDS_PASS_BWIDTH_H_SHIFT 16
#define VI6_UDS_PASS_BWIDTH_V_MASK (0x7f << 0)
#define VI6_UDS_PASS_BWIDTH_V_SHIFT 0
#define VI6_UDS_IPC 0x2318
#define VI6_UDS_IPC_FIELD (1 << 27)
#define VI6_UDS_IPC_VEDP_MASK (0xfff << 0)
#define VI6_UDS_IPC_VEDP_SHIFT 0
#define VI6_UDS_CLIP_SIZE 0x2324
#define VI6_UDS_CLIP_SIZE_HSIZE_MASK (0x1fff << 16)
#define VI6_UDS_CLIP_SIZE_HSIZE_SHIFT 16
#define VI6_UDS_CLIP_SIZE_VSIZE_MASK (0x1fff << 0)
#define VI6_UDS_CLIP_SIZE_VSIZE_SHIFT 0
#define VI6_UDS_FILL_COLOR 0x2328
#define VI6_UDS_FILL_COLOR_RFILC_MASK (0xff << 16)
#define VI6_UDS_FILL_COLOR_RFILC_SHIFT 16
#define VI6_UDS_FILL_COLOR_GFILC_MASK (0xff << 8)
#define VI6_UDS_FILL_COLOR_GFILC_SHIFT 8
#define VI6_UDS_FILL_COLOR_BFILC_MASK (0xff << 0)
#define VI6_UDS_FILL_COLOR_BFILC_SHIFT 0
/* -----------------------------------------------------------------------------
* LUT Control Registers
*/
#define VI6_LUT_CTRL 0x2800
#define VI6_LUT_CTRL_EN (1 << 0)
/* -----------------------------------------------------------------------------
* CLU Control Registers
*/
#define VI6_CLU_CTRL 0x2900
/* -----------------------------------------------------------------------------
* HST Control Registers
*/
#define VI6_HST_CTRL 0x2a00
#define VI6_HST_CTRL_EN (1 << 0)
/* -----------------------------------------------------------------------------
* HSI Control Registers
*/
#define VI6_HSI_CTRL 0x2b00
#define VI6_HSI_CTRL_EN (1 << 0)
/* -----------------------------------------------------------------------------
* BRU Control Registers
*/
#define VI6_ROP_NOP 0
#define VI6_ROP_AND 1
#define VI6_ROP_AND_REV 2
#define VI6_ROP_COPY 3
#define VI6_ROP_AND_INV 4
#define VI6_ROP_CLEAR 5
#define VI6_ROP_XOR 6
#define VI6_ROP_OR 7
#define VI6_ROP_NOR 8
#define VI6_ROP_EQUIV 9
#define VI6_ROP_INVERT 10
#define VI6_ROP_OR_REV 11
#define VI6_ROP_COPY_INV 12
#define VI6_ROP_OR_INV 13
#define VI6_ROP_NAND 14
#define VI6_ROP_SET 15
#define VI6_BRU_INCTRL 0x2c00
#define VI6_BRU_INCTRL_NRM (1 << 28)
#define VI6_BRU_INCTRL_DnON (1 << (16 + (n)))
#define VI6_BRU_INCTRL_DITHn_OFF (0 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_18BPP (1 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_16BPP (2 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_15BPP (3 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_12BPP (4 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_8BPP (5 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_MASK (7 << ((n) * 4))
#define VI6_BRU_INCTRL_DITHn_SHIFT ((n) * 4)
#define VI6_BRU_VIRRPF_SIZE 0x2c04
#define VI6_BRU_VIRRPF_SIZE_HSIZE_MASK (0x1fff << 16)
#define VI6_BRU_VIRRPF_SIZE_HSIZE_SHIFT 16
#define VI6_BRU_VIRRPF_SIZE_VSIZE_MASK (0x1fff << 0)
#define VI6_BRU_VIRRPF_SIZE_VSIZE_SHIFT 0
#define VI6_BRU_VIRRPF_LOC 0x2c08
#define VI6_BRU_VIRRPF_LOC_HCOORD_MASK (0x1fff << 16)
#define VI6_BRU_VIRRPF_LOC_HCOORD_SHIFT 16
#define VI6_BRU_VIRRPF_LOC_VCOORD_MASK (0x1fff << 0)
#define VI6_BRU_VIRRPF_LOC_VCOORD_SHIFT 0
#define VI6_BRU_VIRRPF_COL 0x2c0c
#define VI6_BRU_VIRRPF_COL_A_MASK (0xff << 24)
#define VI6_BRU_VIRRPF_COL_A_SHIFT 24
#define VI6_BRU_VIRRPF_COL_RCR_MASK (0xff << 16)
#define VI6_BRU_VIRRPF_COL_RCR_SHIFT 16
#define VI6_BRU_VIRRPF_COL_GY_MASK (0xff << 8)
#define VI6_BRU_VIRRPF_COL_GY_SHIFT 8
#define VI6_BRU_VIRRPF_COL_BCB_MASK (0xff << 0)
#define VI6_BRU_VIRRPF_COL_BCB_SHIFT 0
#define VI6_BRU_CTRL(n) (0x2c10 + (n) * 8)
#define VI6_BRU_CTRL_RBC (1 << 31)
#define VI6_BRU_CTRL_DSTSEL_BRUIN(n) ((n) << 20)
#define VI6_BRU_CTRL_DSTSEL_VRPF (4 << 20)
#define VI6_BRU_CTRL_DSTSEL_MASK (7 << 20)
#define VI6_BRU_CTRL_SRCSEL_BRUIN(n) ((n) << 16)
#define VI6_BRU_CTRL_SRCSEL_VRPF (4 << 16)
#define VI6_BRU_CTRL_SRCSEL_MASK (7 << 16)
#define VI6_BRU_CTRL_CROP(rop) ((rop) << 4)
#define VI6_BRU_CTRL_CROP_MASK (0xf << 4)
#define VI6_BRU_CTRL_AROP(rop) ((rop) << 0)
#define VI6_BRU_CTRL_AROP_MASK (0xf << 0)
#define VI6_BRU_BLD(n) (0x2c14 + (n) * 8)
#define VI6_BRU_BLD_CBES (1 << 31)
#define VI6_BRU_BLD_CCMDX_DST_A (0 << 28)
#define VI6_BRU_BLD_CCMDX_255_DST_A (1 << 28)
#define VI6_BRU_BLD_CCMDX_SRC_A (2 << 28)
#define VI6_BRU_BLD_CCMDX_255_SRC_A (3 << 28)
#define VI6_BRU_BLD_CCMDX_COEFX (4 << 28)
#define VI6_BRU_BLD_CCMDX_MASK (7 << 28)
#define VI6_BRU_BLD_CCMDY_DST_A (0 << 24)
#define VI6_BRU_BLD_CCMDY_255_DST_A (1 << 24)
#define VI6_BRU_BLD_CCMDY_SRC_A (2 << 24)
#define VI6_BRU_BLD_CCMDY_255_SRC_A (3 << 24)
#define VI6_BRU_BLD_CCMDY_COEFY (4 << 24)
#define VI6_BRU_BLD_CCMDY_MASK (7 << 24)
#define VI6_BRU_BLD_CCMDY_SHIFT 24
#define VI6_BRU_BLD_ABES (1 << 23)
#define VI6_BRU_BLD_ACMDX_DST_A (0 << 20)
#define VI6_BRU_BLD_ACMDX_255_DST_A (1 << 20)
#define VI6_BRU_BLD_ACMDX_SRC_A (2 << 20)
#define VI6_BRU_BLD_ACMDX_255_SRC_A (3 << 20)
#define VI6_BRU_BLD_ACMDX_COEFX (4 << 20)
#define VI6_BRU_BLD_ACMDX_MASK (7 << 20)
#define VI6_BRU_BLD_ACMDY_DST_A (0 << 16)
#define VI6_BRU_BLD_ACMDY_255_DST_A (1 << 16)
#define VI6_BRU_BLD_ACMDY_SRC_A (2 << 16)
#define VI6_BRU_BLD_ACMDY_255_SRC_A (3 << 16)
#define VI6_BRU_BLD_ACMDY_COEFY (4 << 16)
#define VI6_BRU_BLD_ACMDY_MASK (7 << 16)
#define VI6_BRU_BLD_COEFX_MASK (0xff << 8)
#define VI6_BRU_BLD_COEFX_SHIFT 8
#define VI6_BRU_BLD_COEFY_MASK (0xff << 0)
#define VI6_BRU_BLD_COEFY_SHIFT 0
#define VI6_BRU_ROP 0x2c30
#define VI6_BRU_ROP_DSTSEL_BRUIN(n) ((n) << 20)
#define VI6_BRU_ROP_DSTSEL_VRPF (4 << 20)
#define VI6_BRU_ROP_DSTSEL_MASK (7 << 20)
#define VI6_BRU_ROP_CROP(rop) ((rop) << 4)
#define VI6_BRU_ROP_CROP_MASK (0xf << 4)
#define VI6_BRU_ROP_AROP(rop) ((rop) << 0)
#define VI6_BRU_ROP_AROP_MASK (0xf << 0)
/* -----------------------------------------------------------------------------
* HGO Control Registers
*/
#define VI6_HGO_OFFSET 0x3000
#define VI6_HGO_SIZE 0x3004
#define VI6_HGO_MODE 0x3008
#define VI6_HGO_LB_TH 0x300c
#define VI6_HGO_LBn_H(n) (0x3010 + (n) * 8)
#define VI6_HGO_LBn_V(n) (0x3014 + (n) * 8)
#define VI6_HGO_R_HISTO 0x3030
#define VI6_HGO_R_MAXMIN 0x3130
#define VI6_HGO_R_SUM 0x3134
#define VI6_HGO_R_LB_DET 0x3138
#define VI6_HGO_G_HISTO 0x3140
#define VI6_HGO_G_MAXMIN 0x3240
#define VI6_HGO_G_SUM 0x3244
#define VI6_HGO_G_LB_DET 0x3248
#define VI6_HGO_B_HISTO 0x3250
#define VI6_HGO_B_MAXMIN 0x3350
#define VI6_HGO_B_SUM 0x3354
#define VI6_HGO_B_LB_DET 0x3358
#define VI6_HGO_REGRST 0x33fc
/* -----------------------------------------------------------------------------
* HGT Control Registers
*/
#define VI6_HGT_OFFSET 0x3400
#define VI6_HGT_SIZE 0x3404
#define VI6_HGT_MODE 0x3408
#define VI6_HGT_HUE_AREA(n) (0x340c + (n) * 4)
#define VI6_HGT_LB_TH 0x3424
#define VI6_HGT_LBn_H(n) (0x3438 + (n) * 8)
#define VI6_HGT_LBn_V(n) (0x342c + (n) * 8)
#define VI6_HGT_HISTO(m, n) (0x3450 + (m) * 128 + (n) * 4)
#define VI6_HGT_MAXMIN 0x3750
#define VI6_HGT_SUM 0x3754
#define VI6_HGT_LB_DET 0x3758
#define VI6_HGT_REGRST 0x37fc
/* -----------------------------------------------------------------------------
* LIF Control Registers
*/
#define VI6_LIF_CTRL 0x3b00
#define VI6_LIF_CTRL_OBTH_MASK (0x7ff << 16)
#define VI6_LIF_CTRL_OBTH_SHIFT 16
#define VI6_LIF_CTRL_CFMT (1 << 4)
#define VI6_LIF_CTRL_REQSEL (1 << 1)
#define VI6_LIF_CTRL_LIF_EN (1 << 0)
#define VI6_LIF_CSBTH 0x3b04
#define VI6_LIF_CSBTH_HBTH_MASK (0x7ff << 16)
#define VI6_LIF_CSBTH_HBTH_SHIFT 16
#define VI6_LIF_CSBTH_LBTH_MASK (0x7ff << 0)
#define VI6_LIF_CSBTH_LBTH_SHIFT 0
/* -----------------------------------------------------------------------------
* Security Control Registers
*/
#define VI6_SECURITY_CTRL0 0x3d00
#define VI6_SECURITY_CTRL1 0x3d04
/* -----------------------------------------------------------------------------
* RPF CLUT Registers
*/
#define VI6_CLUT_TABLE 0x4000
/* -----------------------------------------------------------------------------
* 1D LUT Registers
*/
#define VI6_LUT_TABLE 0x7000
/* -----------------------------------------------------------------------------
* 3D LUT Registers
*/
#define VI6_CLU_ADDR 0x7400
#define VI6_CLU_DATA 0x7404
/* -----------------------------------------------------------------------------
* Formats
*/
#define VI6_FMT_RGB_332 0x00
#define VI6_FMT_XRGB_4444 0x01
#define VI6_FMT_RGBX_4444 0x02
#define VI6_FMT_XRGB_1555 0x04
#define VI6_FMT_RGBX_5551 0x05
#define VI6_FMT_RGB_565 0x06
#define VI6_FMT_AXRGB_86666 0x07
#define VI6_FMT_RGBXA_66668 0x08
#define VI6_FMT_XRGBA_66668 0x09
#define VI6_FMT_ARGBX_86666 0x0a
#define VI6_FMT_AXRXGXB_8262626 0x0b
#define VI6_FMT_XRXGXBA_2626268 0x0c
#define VI6_FMT_ARXGXBX_8626262 0x0d
#define VI6_FMT_RXGXBXA_6262628 0x0e
#define VI6_FMT_XRGB_6666 0x0f
#define VI6_FMT_RGBX_6666 0x10
#define VI6_FMT_XRXGXB_262626 0x11
#define VI6_FMT_RXGXBX_626262 0x12
#define VI6_FMT_ARGB_8888 0x13
#define VI6_FMT_RGBA_8888 0x14
#define VI6_FMT_RGB_888 0x15
#define VI6_FMT_XRGXGB_763763 0x16
#define VI6_FMT_XXRGB_86666 0x17
#define VI6_FMT_BGR_888 0x18
#define VI6_FMT_ARGB_4444 0x19
#define VI6_FMT_RGBA_4444 0x1a
#define VI6_FMT_ARGB_1555 0x1b
#define VI6_FMT_RGBA_5551 0x1c
#define VI6_FMT_ABGR_4444 0x1d
#define VI6_FMT_BGRA_4444 0x1e
#define VI6_FMT_ABGR_1555 0x1f
#define VI6_FMT_BGRA_5551 0x20
#define VI6_FMT_XBXGXR_262626 0x21
#define VI6_FMT_ABGR_8888 0x22
#define VI6_FMT_XXRGB_88565 0x23
#define VI6_FMT_Y_UV_444 0x40
#define VI6_FMT_Y_UV_422 0x41
#define VI6_FMT_Y_UV_420 0x42
#define VI6_FMT_YUV_444 0x46
#define VI6_FMT_YUYV_422 0x47
#define VI6_FMT_YYUV_422 0x48
#define VI6_FMT_YUV_420 0x49
#define VI6_FMT_Y_U_V_444 0x4a
#define VI6_FMT_Y_U_V_422 0x4b
#define VI6_FMT_Y_U_V_420 0x4c
#endif /* __VSP1_REGS_H__ */

View file

@ -0,0 +1,276 @@
/*
* vsp1_rpf.c -- R-Car VSP1 Read Pixel Formatter
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define RPF_MAX_WIDTH 8190
#define RPF_MAX_HEIGHT 8190
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_rpf_read(struct vsp1_rwpf *rpf, u32 reg)
{
return vsp1_read(rpf->entity.vsp1,
reg + rpf->entity.index * VI6_RPF_OFFSET);
}
static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf, u32 reg, u32 data)
{
vsp1_write(rpf->entity.vsp1,
reg + rpf->entity.index * VI6_RPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
static int rpf_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_rwpf *rpf =
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
struct vsp1_pipeline *pipe;
if (!vsp1_entity_is_streaming(&rpf->entity))
return 0;
switch (ctrl->id) {
case V4L2_CID_ALPHA_COMPONENT:
vsp1_rpf_write(rpf, VI6_RPF_VRTCOL_SET,
ctrl->val << VI6_RPF_VRTCOL_SET_LAYA_SHIFT);
pipe = to_vsp1_pipeline(&rpf->entity.subdev.entity);
vsp1_pipeline_propagate_alpha(pipe, &rpf->entity, ctrl->val);
break;
}
return 0;
}
static const struct v4l2_ctrl_ops rpf_ctrl_ops = {
.s_ctrl = rpf_s_ctrl,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int rpf_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_rwpf *rpf = to_rwpf(subdev);
const struct vsp1_format_info *fmtinfo = rpf->video.fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->video.format;
const struct v4l2_rect *crop = &rpf->crop;
u32 pstride;
u32 infmt;
int ret;
ret = vsp1_entity_set_streaming(&rpf->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
/* Source size, stride and crop offsets.
*
* The crop offsets correspond to the location of the crop rectangle top
* left corner in the plane buffer. Only two offsets are needed, as
* planes 2 and 3 always have identical strides.
*/
vsp1_rpf_write(rpf, VI6_RPF_SRC_BSIZE,
(crop->width << VI6_RPF_SRC_BSIZE_BHSIZE_SHIFT) |
(crop->height << VI6_RPF_SRC_BSIZE_BVSIZE_SHIFT));
vsp1_rpf_write(rpf, VI6_RPF_SRC_ESIZE,
(crop->width << VI6_RPF_SRC_ESIZE_EHSIZE_SHIFT) |
(crop->height << VI6_RPF_SRC_ESIZE_EVSIZE_SHIFT));
rpf->offsets[0] = crop->top * format->plane_fmt[0].bytesperline
+ crop->left * fmtinfo->bpp[0] / 8;
pstride = format->plane_fmt[0].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_Y_SHIFT;
if (format->num_planes > 1) {
rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline
+ crop->left * fmtinfo->bpp[1] / 8;
pstride |= format->plane_fmt[1].bytesperline
<< VI6_RPF_SRCM_PSTRIDE_C_SHIFT;
}
vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
infmt = VI6_RPF_INFMT_CIPM
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
if (fmtinfo->swap_yc)
infmt |= VI6_RPF_INFMT_SPYCS;
if (fmtinfo->swap_uv)
infmt |= VI6_RPF_INFMT_SPUVS;
if (rpf->entity.formats[RWPF_PAD_SINK].code !=
rpf->entity.formats[RWPF_PAD_SOURCE].code)
infmt |= VI6_RPF_INFMT_CSC;
vsp1_rpf_write(rpf, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, VI6_RPF_DSWAP, fmtinfo->swap);
/* Output location */
vsp1_rpf_write(rpf, VI6_RPF_LOC,
(rpf->location.left << VI6_RPF_LOC_HCOORD_SHIFT) |
(rpf->location.top << VI6_RPF_LOC_VCOORD_SHIFT));
/* Use the alpha channel (extended to 8 bits) when available or an
* alpha value set through the V4L2_CID_ALPHA_COMPONENT control
* otherwise. Disable color keying.
*/
vsp1_rpf_write(rpf, VI6_RPF_ALPH_SEL, VI6_RPF_ALPH_SEL_AEXT_EXT |
(fmtinfo->alpha ? VI6_RPF_ALPH_SEL_ASEL_PACKED
: VI6_RPF_ALPH_SEL_ASEL_FIXED));
vsp1_rpf_write(rpf, VI6_RPF_MSK_CTRL, 0);
vsp1_rpf_write(rpf, VI6_RPF_CKEY_CTRL, 0);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops rpf_video_ops = {
.s_stream = rpf_s_stream,
};
static struct v4l2_subdev_pad_ops rpf_pad_ops = {
.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
.enum_frame_size = vsp1_rwpf_enum_frame_size,
.get_fmt = vsp1_rwpf_get_format,
.set_fmt = vsp1_rwpf_set_format,
.get_selection = vsp1_rwpf_get_selection,
.set_selection = vsp1_rwpf_set_selection,
};
static struct v4l2_subdev_ops rpf_ops = {
.video = &rpf_video_ops,
.pad = &rpf_pad_ops,
};
/* -----------------------------------------------------------------------------
* Video Device Operations
*/
static void rpf_vdev_queue(struct vsp1_video *video,
struct vsp1_video_buffer *buf)
{
struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video);
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y,
buf->addr[0] + rpf->offsets[0]);
if (buf->buf.num_planes > 1)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0,
buf->addr[1] + rpf->offsets[1]);
if (buf->buf.num_planes > 2)
vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1,
buf->addr[2] + rpf->offsets[1]);
}
static const struct vsp1_video_operations rpf_vdev_ops = {
.queue = rpf_vdev_queue,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_video *video;
struct vsp1_rwpf *rpf;
int ret;
rpf = devm_kzalloc(vsp1->dev, sizeof(*rpf), GFP_KERNEL);
if (rpf == NULL)
return ERR_PTR(-ENOMEM);
rpf->max_width = RPF_MAX_WIDTH;
rpf->max_height = RPF_MAX_HEIGHT;
rpf->entity.type = VSP1_ENTITY_RPF;
rpf->entity.index = index;
ret = vsp1_entity_init(vsp1, &rpf->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &rpf->entity.subdev;
v4l2_subdev_init(subdev, &rpf_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s rpf.%u",
dev_name(vsp1->dev), index);
v4l2_set_subdevdata(subdev, rpf);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&rpf->ctrls, 1);
v4l2_ctrl_new_std(&rpf->ctrls, &rpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
0, 255, 1, 255);
rpf->entity.subdev.ctrl_handler = &rpf->ctrls;
if (rpf->ctrls.error) {
dev_err(vsp1->dev, "rpf%u: failed to initialize controls\n",
index);
ret = rpf->ctrls.error;
goto error;
}
/* Initialize the video device. */
video = &rpf->video;
video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
video->vsp1 = vsp1;
video->ops = &rpf_vdev_ops;
ret = vsp1_video_init(video, &rpf->entity);
if (ret < 0)
goto error;
rpf->entity.video = video;
/* Connect the video device to the RPF. */
ret = media_entity_create_link(&rpf->video.video.entity, 0,
&rpf->entity.subdev.entity,
RWPF_PAD_SINK,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (ret < 0)
goto error;
return rpf;
error:
vsp1_entity_destroy(&rpf->entity);
return ERR_PTR(ret);
}

View file

@ -0,0 +1,220 @@
/*
* vsp1_rwpf.c -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define RWPF_MIN_WIDTH 1
#define RWPF_MIN_HEIGHT 1
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
return 0;
}
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, fse->pad);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == RWPF_PAD_SINK) {
fse->min_width = RWPF_MIN_WIDTH;
fse->max_width = rwpf->max_width;
fse->min_height = RWPF_MIN_HEIGHT;
fse->max_height = rwpf->max_height;
} else {
/* The size on the source pad are fixed and always identical to
* the size on the sink pad.
*/
fse->min_width = format->width;
fse->max_width = format->width;
fse->min_height = format->height;
fse->max_height = format->height;
}
return 0;
}
static struct v4l2_rect *
vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf, struct v4l2_subdev_fh *fh, u32 which)
{
switch (which) {
case V4L2_SUBDEV_FORMAT_TRY:
return v4l2_subdev_get_try_crop(fh, RWPF_PAD_SINK);
case V4L2_SUBDEV_FORMAT_ACTIVE:
return &rwpf->crop;
default:
return NULL;
}
}
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
fmt->format = *vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
fmt->which);
return 0;
}
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->format.code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->format.code = V4L2_MBUS_FMT_AYUV8_1X32;
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, fmt->pad,
fmt->which);
if (fmt->pad == RWPF_PAD_SOURCE) {
/* The RWPF performs format conversion but can't scale, only the
* format code can be changed on the source pad.
*/
format->code = fmt->format.code;
fmt->format = *format;
return 0;
}
format->code = fmt->format.code;
format->width = clamp_t(unsigned int, fmt->format.width,
RWPF_MIN_WIDTH, rwpf->max_width);
format->height = clamp_t(unsigned int, fmt->format.height,
RWPF_MIN_HEIGHT, rwpf->max_height);
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
fmt->format = *format;
/* Update the sink crop rectangle. */
crop = vsp1_rwpf_get_crop(rwpf, fh, fmt->which);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
crop->height = fmt->format.height;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
fmt->which);
*format = fmt->format;
return 0;
}
int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
/* Cropping is implemented on the sink pad. */
if (sel->pad != RWPF_PAD_SINK)
return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
sel->r = *vsp1_rwpf_get_crop(rwpf, fh, sel->which);
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
format = vsp1_entity_get_pad_format(&rwpf->entity, fh,
RWPF_PAD_SINK, sel->which);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = format->width;
sel->r.height = format->height;
break;
default:
return -EINVAL;
}
return 0;
}
int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel)
{
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
/* Cropping is implemented on the sink pad. */
if (sel->pad != RWPF_PAD_SINK)
return -EINVAL;
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
/* Make sure the crop rectangle is entirely contained in the image. The
* WPF top and left offsets are limited to 255.
*/
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SINK,
sel->which);
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) {
sel->r.left = min_t(unsigned int, sel->r.left, 255);
sel->r.top = min_t(unsigned int, sel->r.top, 255);
}
sel->r.width = min_t(unsigned int, sel->r.width,
format->width - sel->r.left);
sel->r.height = min_t(unsigned int, sel->r.height,
format->height - sel->r.top);
crop = vsp1_rwpf_get_crop(rwpf, fh, sel->which);
*crop = sel->r;
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&rwpf->entity, fh, RWPF_PAD_SOURCE,
sel->which);
format->width = crop->width;
format->height = crop->height;
return 0;
}

View file

@ -0,0 +1,69 @@
/*
* vsp1_rwpf.h -- R-Car VSP1 Read and Write Pixel Formatters
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_RWPF_H__
#define __VSP1_RWPF_H__
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_entity.h"
#include "vsp1_video.h"
#define RWPF_PAD_SINK 0
#define RWPF_PAD_SOURCE 1
struct vsp1_rwpf {
struct vsp1_entity entity;
struct vsp1_video video;
struct v4l2_ctrl_handler ctrls;
unsigned int max_width;
unsigned int max_height;
struct {
unsigned int left;
unsigned int top;
} location;
struct v4l2_rect crop;
unsigned int offsets[2];
};
static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_rwpf, entity.subdev);
}
struct vsp1_rwpf *vsp1_rpf_create(struct vsp1_device *vsp1, unsigned int index);
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index);
int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code);
int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse);
int vsp1_rwpf_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt);
int vsp1_rwpf_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt);
int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel);
int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_selection *sel);
#endif /* __VSP1_RWPF_H__ */

View file

@ -0,0 +1,382 @@
/*
* vsp1_sru.c -- R-Car VSP1 Super Resolution Unit
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_sru.h"
#define SRU_MIN_SIZE 4U
#define SRU_MAX_SIZE 8190U
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_sru_read(struct vsp1_sru *sru, u32 reg)
{
return vsp1_read(sru->entity.vsp1, reg);
}
static inline void vsp1_sru_write(struct vsp1_sru *sru, u32 reg, u32 data)
{
vsp1_write(sru->entity.vsp1, reg, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
#define V4L2_CID_VSP1_SRU_INTENSITY (V4L2_CID_USER_BASE + 1)
struct vsp1_sru_param {
u32 ctrl0;
u32 ctrl2;
};
#define VI6_SRU_CTRL0_PARAMS(p0, p1) \
(((p0) << VI6_SRU_CTRL0_PARAM0_SHIFT) | \
((p1) << VI6_SRU_CTRL0_PARAM1_SHIFT))
#define VI6_SRU_CTRL2_PARAMS(p6, p7, p8) \
(((p6) << VI6_SRU_CTRL2_PARAM6_SHIFT) | \
((p7) << VI6_SRU_CTRL2_PARAM7_SHIFT) | \
((p8) << VI6_SRU_CTRL2_PARAM8_SHIFT))
static const struct vsp1_sru_param vsp1_sru_params[] = {
{
.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(24, 40, 255),
}, {
.ctrl0 = VI6_SRU_CTRL0_PARAMS(256, 4) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(8, 16, 255),
}, {
.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(36, 60, 255),
}, {
.ctrl0 = VI6_SRU_CTRL0_PARAMS(384, 5) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(12, 27, 255),
}, {
.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(48, 80, 255),
}, {
.ctrl0 = VI6_SRU_CTRL0_PARAMS(511, 6) | VI6_SRU_CTRL0_EN,
.ctrl2 = VI6_SRU_CTRL2_PARAMS(16, 36, 255),
},
};
static int sru_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_sru *sru =
container_of(ctrl->handler, struct vsp1_sru, ctrls);
const struct vsp1_sru_param *param;
u32 value;
switch (ctrl->id) {
case V4L2_CID_VSP1_SRU_INTENSITY:
param = &vsp1_sru_params[ctrl->val - 1];
value = vsp1_sru_read(sru, VI6_SRU_CTRL0);
value &= ~(VI6_SRU_CTRL0_PARAM0_MASK |
VI6_SRU_CTRL0_PARAM1_MASK);
value |= param->ctrl0;
vsp1_sru_write(sru, VI6_SRU_CTRL0, value);
vsp1_sru_write(sru, VI6_SRU_CTRL2, param->ctrl2);
break;
}
return 0;
}
static const struct v4l2_ctrl_ops sru_ctrl_ops = {
.s_ctrl = sru_s_ctrl,
};
static const struct v4l2_ctrl_config sru_intensity_control = {
.ops = &sru_ctrl_ops,
.id = V4L2_CID_VSP1_SRU_INTENSITY,
.name = "Intensity",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 1,
.max = 6,
.def = 1,
.step = 1,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int sru_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
int ret;
ret = vsp1_entity_set_streaming(&sru->entity, enable);
if (ret < 0)
return ret;
if (!enable)
return 0;
input = &sru->entity.formats[SRU_PAD_SINK];
output = &sru->entity.formats[SRU_PAD_SOURCE];
if (input->code == V4L2_MBUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
| VI6_SRU_CTRL0_PARAM4;
else
ctrl0 = VI6_SRU_CTRL0_PARAM3;
if (input->width != output->width)
ctrl0 |= VI6_SRU_CTRL0_MODE_UPSCALE;
/* Take the control handler lock to ensure that the CTRL0 value won't be
* changed behind our back by a set control operation.
*/
mutex_lock(sru->ctrls.lock);
ctrl0 |= vsp1_sru_read(sru, VI6_SRU_CTRL0)
& (VI6_SRU_CTRL0_PARAM0_MASK | VI6_SRU_CTRL0_PARAM1_MASK);
mutex_unlock(sru->ctrls.lock);
vsp1_sru_write(sru, VI6_SRU_CTRL1, VI6_SRU_CTRL1_PARAM5);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int sru_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
struct v4l2_mbus_framefmt *format;
if (code->pad == SRU_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
/* The SRU can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int sru_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, SRU_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == SRU_PAD_SINK) {
fse->min_width = SRU_MIN_SIZE;
fse->max_width = SRU_MAX_SIZE;
fse->min_height = SRU_MIN_SIZE;
fse->max_height = SRU_MAX_SIZE;
} else {
fse->min_width = format->width;
fse->min_height = format->height;
if (format->width <= SRU_MAX_SIZE / 2 &&
format->height <= SRU_MAX_SIZE / 2) {
fse->max_width = format->width * 2;
fse->max_height = format->height * 2;
} else {
fse->max_width = format->width;
fse->max_height = format->height;
}
}
return 0;
}
static int sru_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_sru *sru = to_sru(subdev);
fmt->format = *vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static void sru_try_format(struct vsp1_sru *sru, struct v4l2_subdev_fh *fh,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
unsigned int input_area;
unsigned int output_area;
switch (pad) {
case SRU_PAD_SINK:
/* Default to YUV if the requested format is not supported. */
if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
break;
case SRU_PAD_SOURCE:
/* The SRU can't perform format conversion. */
format = vsp1_entity_get_pad_format(&sru->entity, fh,
SRU_PAD_SINK, which);
fmt->code = format->code;
/* We can upscale by 2 in both direction, but not independently.
* Compare the input and output rectangles areas (avoiding
* integer overflows on the output): if the requested output
* area is larger than 1.5^2 the input area upscale by two,
* otherwise don't scale.
*/
input_area = format->width * format->height;
output_area = min(fmt->width, SRU_MAX_SIZE)
* min(fmt->height, SRU_MAX_SIZE);
if (fmt->width <= SRU_MAX_SIZE / 2 &&
fmt->height <= SRU_MAX_SIZE / 2 &&
output_area > input_area * 9 / 4) {
fmt->width = format->width * 2;
fmt->height = format->height * 2;
} else {
fmt->width = format->width;
fmt->height = format->height;
}
break;
}
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int sru_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_mbus_framefmt *format;
sru_try_format(sru, fh, fmt->pad, &fmt->format, fmt->which);
format = vsp1_entity_get_pad_format(&sru->entity, fh, fmt->pad,
fmt->which);
*format = fmt->format;
if (fmt->pad == SRU_PAD_SINK) {
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&sru->entity, fh,
SRU_PAD_SOURCE, fmt->which);
*format = fmt->format;
sru_try_format(sru, fh, SRU_PAD_SOURCE, format, fmt->which);
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops sru_video_ops = {
.s_stream = sru_s_stream,
};
static struct v4l2_subdev_pad_ops sru_pad_ops = {
.enum_mbus_code = sru_enum_mbus_code,
.enum_frame_size = sru_enum_frame_size,
.get_fmt = sru_get_format,
.set_fmt = sru_set_format,
};
static struct v4l2_subdev_ops sru_ops = {
.video = &sru_video_ops,
.pad = &sru_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1)
{
struct v4l2_subdev *subdev;
struct vsp1_sru *sru;
int ret;
sru = devm_kzalloc(vsp1->dev, sizeof(*sru), GFP_KERNEL);
if (sru == NULL)
return ERR_PTR(-ENOMEM);
sru->entity.type = VSP1_ENTITY_SRU;
ret = vsp1_entity_init(vsp1, &sru->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &sru->entity.subdev;
v4l2_subdev_init(subdev, &sru_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s sru",
dev_name(vsp1->dev));
v4l2_set_subdevdata(subdev, sru);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&sru->ctrls, 1);
v4l2_ctrl_new_custom(&sru->ctrls, &sru_intensity_control, NULL);
sru->entity.subdev.ctrl_handler = &sru->ctrls;
if (sru->ctrls.error) {
dev_err(vsp1->dev, "sru: failed to initialize controls\n");
ret = sru->ctrls.error;
vsp1_entity_destroy(&sru->entity);
return ERR_PTR(ret);
}
return sru;
}

View file

@ -0,0 +1,40 @@
/*
* vsp1_sru.h -- R-Car VSP1 Super Resolution Unit
*
* Copyright (C) 2013 Renesas Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_SRU_H__
#define __VSP1_SRU_H__
#include <media/media-entity.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define SRU_PAD_SINK 0
#define SRU_PAD_SOURCE 1
struct vsp1_sru {
struct vsp1_entity entity;
struct v4l2_ctrl_handler ctrls;
};
static inline struct vsp1_sru *to_sru(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_sru, entity.subdev);
}
struct vsp1_sru *vsp1_sru_create(struct vsp1_device *vsp1);
#endif /* __VSP1_SRU_H__ */

View file

@ -0,0 +1,353 @@
/*
* vsp1_uds.c -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/gfp.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_uds.h"
#define UDS_MIN_SIZE 4U
#define UDS_MAX_SIZE 8190U
#define UDS_MIN_FACTOR 0x0100
#define UDS_MAX_FACTOR 0xffff
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_uds_read(struct vsp1_uds *uds, u32 reg)
{
return vsp1_read(uds->entity.vsp1,
reg + uds->entity.index * VI6_UDS_OFFSET);
}
static inline void vsp1_uds_write(struct vsp1_uds *uds, u32 reg, u32 data)
{
vsp1_write(uds->entity.vsp1,
reg + uds->entity.index * VI6_UDS_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Scaling Computation
*/
void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha)
{
vsp1_uds_write(uds, VI6_UDS_ALPVAL, alpha << VI6_UDS_ALPVAL_VAL0_SHIFT);
}
/*
* uds_output_size - Return the output size for an input size and scaling ratio
* @input: input size in pixels
* @ratio: scaling ratio in U4.12 fixed-point format
*/
static unsigned int uds_output_size(unsigned int input, unsigned int ratio)
{
if (ratio > 4096) {
/* Down-scaling */
unsigned int mp;
mp = ratio / 4096;
mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
return (input - 1) / mp * mp * 4096 / ratio + 1;
} else {
/* Up-scaling */
return (input - 1) * 4096 / ratio + 1;
}
}
/*
* uds_output_limits - Return the min and max output sizes for an input size
* @input: input size in pixels
* @minimum: minimum output size (returned)
* @maximum: maximum output size (returned)
*/
static void uds_output_limits(unsigned int input,
unsigned int *minimum, unsigned int *maximum)
{
*minimum = max(uds_output_size(input, UDS_MAX_FACTOR), UDS_MIN_SIZE);
*maximum = min(uds_output_size(input, UDS_MIN_FACTOR), UDS_MAX_SIZE);
}
/*
* uds_passband_width - Return the passband filter width for a scaling ratio
* @ratio: scaling ratio in U4.12 fixed-point format
*/
static unsigned int uds_passband_width(unsigned int ratio)
{
if (ratio >= 4096) {
/* Down-scaling */
unsigned int mp;
mp = ratio / 4096;
mp = mp < 4 ? 1 : (mp < 8 ? 2 : 4);
return 64 * 4096 * mp / ratio;
} else {
/* Up-scaling */
return 64;
}
}
static unsigned int uds_compute_ratio(unsigned int input, unsigned int output)
{
/* TODO: This is an approximation that will need to be refined. */
return (input - 1) * 4096 / (output - 1);
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int uds_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_uds *uds = to_uds(subdev);
const struct v4l2_mbus_framefmt *output;
const struct v4l2_mbus_framefmt *input;
unsigned int hscale;
unsigned int vscale;
bool multitap;
if (!enable)
return 0;
input = &uds->entity.formats[UDS_PAD_SINK];
output = &uds->entity.formats[UDS_PAD_SOURCE];
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
dev_dbg(uds->entity.vsp1->dev, "hscale %u vscale %u\n", hscale, vscale);
/* Multi-tap scaling can't be enabled along with alpha scaling when
* scaling down with a factor lower than or equal to 1/2 in either
* direction.
*/
if (uds->scale_alpha && (hscale >= 8192 || vscale >= 8192))
multitap = false;
else
multitap = true;
vsp1_uds_write(uds, VI6_UDS_CTRL,
(uds->scale_alpha ? VI6_UDS_CTRL_AON : 0) |
(multitap ? VI6_UDS_CTRL_BC : 0));
vsp1_uds_write(uds, VI6_UDS_PASS_BWIDTH,
(uds_passband_width(hscale)
<< VI6_UDS_PASS_BWIDTH_H_SHIFT) |
(uds_passband_width(vscale)
<< VI6_UDS_PASS_BWIDTH_V_SHIFT));
/* Set the scaling ratios and the output size. */
vsp1_uds_write(uds, VI6_UDS_SCALE,
(hscale << VI6_UDS_SCALE_HFRAC_SHIFT) |
(vscale << VI6_UDS_SCALE_VFRAC_SHIFT));
vsp1_uds_write(uds, VI6_UDS_CLIP_SIZE,
(output->width << VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
(output->height << VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Pad Operations
*/
static int uds_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_mbus_code_enum *code)
{
static const unsigned int codes[] = {
V4L2_MBUS_FMT_ARGB8888_1X32,
V4L2_MBUS_FMT_AYUV8_1X32,
};
if (code->pad == UDS_PAD_SINK) {
if (code->index >= ARRAY_SIZE(codes))
return -EINVAL;
code->code = codes[code->index];
} else {
struct v4l2_mbus_framefmt *format;
/* The UDS can't perform format conversion, the sink format is
* always identical to the source format.
*/
if (code->index)
return -EINVAL;
format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
code->code = format->code;
}
return 0;
}
static int uds_enum_frame_size(struct v4l2_subdev *subdev,
struct v4l2_subdev_fh *fh,
struct v4l2_subdev_frame_size_enum *fse)
{
struct v4l2_mbus_framefmt *format;
format = v4l2_subdev_get_try_format(fh, UDS_PAD_SINK);
if (fse->index || fse->code != format->code)
return -EINVAL;
if (fse->pad == UDS_PAD_SINK) {
fse->min_width = UDS_MIN_SIZE;
fse->max_width = UDS_MAX_SIZE;
fse->min_height = UDS_MIN_SIZE;
fse->max_height = UDS_MAX_SIZE;
} else {
uds_output_limits(format->width, &fse->min_width,
&fse->max_width);
uds_output_limits(format->height, &fse->min_height,
&fse->max_height);
}
return 0;
}
static int uds_get_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
fmt->format = *vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
fmt->which);
return 0;
}
static void uds_try_format(struct vsp1_uds *uds, struct v4l2_subdev_fh *fh,
unsigned int pad, struct v4l2_mbus_framefmt *fmt,
enum v4l2_subdev_format_whence which)
{
struct v4l2_mbus_framefmt *format;
unsigned int minimum;
unsigned int maximum;
switch (pad) {
case UDS_PAD_SINK:
/* Default to YUV if the requested format is not supported. */
if (fmt->code != V4L2_MBUS_FMT_ARGB8888_1X32 &&
fmt->code != V4L2_MBUS_FMT_AYUV8_1X32)
fmt->code = V4L2_MBUS_FMT_AYUV8_1X32;
fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
break;
case UDS_PAD_SOURCE:
/* The UDS scales but can't perform format conversion. */
format = vsp1_entity_get_pad_format(&uds->entity, fh,
UDS_PAD_SINK, which);
fmt->code = format->code;
uds_output_limits(format->width, &minimum, &maximum);
fmt->width = clamp(fmt->width, minimum, maximum);
uds_output_limits(format->height, &minimum, &maximum);
fmt->height = clamp(fmt->height, minimum, maximum);
break;
}
fmt->field = V4L2_FIELD_NONE;
fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int uds_set_format(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh,
struct v4l2_subdev_format *fmt)
{
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_mbus_framefmt *format;
uds_try_format(uds, fh, fmt->pad, &fmt->format, fmt->which);
format = vsp1_entity_get_pad_format(&uds->entity, fh, fmt->pad,
fmt->which);
*format = fmt->format;
if (fmt->pad == UDS_PAD_SINK) {
/* Propagate the format to the source pad. */
format = vsp1_entity_get_pad_format(&uds->entity, fh,
UDS_PAD_SOURCE, fmt->which);
*format = fmt->format;
uds_try_format(uds, fh, UDS_PAD_SOURCE, format, fmt->which);
}
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops uds_video_ops = {
.s_stream = uds_s_stream,
};
static struct v4l2_subdev_pad_ops uds_pad_ops = {
.enum_mbus_code = uds_enum_mbus_code,
.enum_frame_size = uds_enum_frame_size,
.get_fmt = uds_get_format,
.set_fmt = uds_set_format,
};
static struct v4l2_subdev_ops uds_ops = {
.video = &uds_video_ops,
.pad = &uds_pad_ops,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_uds *uds;
int ret;
uds = devm_kzalloc(vsp1->dev, sizeof(*uds), GFP_KERNEL);
if (uds == NULL)
return ERR_PTR(-ENOMEM);
uds->entity.type = VSP1_ENTITY_UDS;
uds->entity.index = index;
ret = vsp1_entity_init(vsp1, &uds->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &uds->entity.subdev;
v4l2_subdev_init(subdev, &uds_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s uds.%u",
dev_name(vsp1->dev), index);
v4l2_set_subdevdata(subdev, uds);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
return uds;
}

View file

@ -0,0 +1,40 @@
/*
* vsp1_uds.h -- R-Car VSP1 Up and Down Scaler
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_UDS_H__
#define __VSP1_UDS_H__
#include <media/media-entity.h>
#include <media/v4l2-subdev.h>
#include "vsp1_entity.h"
struct vsp1_device;
#define UDS_PAD_SINK 0
#define UDS_PAD_SOURCE 1
struct vsp1_uds {
struct vsp1_entity entity;
bool scale_alpha;
};
static inline struct vsp1_uds *to_uds(struct v4l2_subdev *subdev)
{
return container_of(subdev, struct vsp1_uds, entity.subdev);
}
struct vsp1_uds *vsp1_uds_create(struct vsp1_device *vsp1, unsigned int index);
void vsp1_uds_set_alpha(struct vsp1_uds *uds, unsigned int alpha);
#endif /* __VSP1_UDS_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,152 @@
/*
* vsp1_video.h -- R-Car VSP1 Video Node
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __VSP1_VIDEO_H__
#define __VSP1_VIDEO_H__
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <media/media-entity.h>
#include <media/videobuf2-core.h>
struct vsp1_video;
/*
* struct vsp1_format_info - VSP1 video format description
* @mbus: media bus format code
* @fourcc: V4L2 pixel format FCC identifier
* @planes: number of planes
* @bpp: bits per pixel
* @hwfmt: VSP1 hardware format
* @swap_yc: the Y and C components are swapped (Y comes before C)
* @swap_uv: the U and V components are swapped (V comes before U)
* @hsub: horizontal subsampling factor
* @vsub: vertical subsampling factor
* @alpha: has an alpha channel
*/
struct vsp1_format_info {
u32 fourcc;
unsigned int mbus;
unsigned int hwfmt;
unsigned int swap;
unsigned int planes;
unsigned int bpp[3];
bool swap_yc;
bool swap_uv;
unsigned int hsub;
unsigned int vsub;
bool alpha;
};
enum vsp1_pipeline_state {
VSP1_PIPELINE_STOPPED,
VSP1_PIPELINE_RUNNING,
VSP1_PIPELINE_STOPPING,
};
/*
* struct vsp1_pipeline - A VSP1 hardware pipeline
* @media: the media pipeline
* @irqlock: protects the pipeline state
* @lock: protects the pipeline use count and stream count
*/
struct vsp1_pipeline {
struct media_pipeline pipe;
spinlock_t irqlock;
enum vsp1_pipeline_state state;
wait_queue_head_t wq;
struct mutex lock;
unsigned int use_count;
unsigned int stream_count;
unsigned int buffers_ready;
unsigned int num_video;
unsigned int num_inputs;
struct vsp1_rwpf *inputs[VSP1_MAX_RPF];
struct vsp1_rwpf *output;
struct vsp1_entity *bru;
struct vsp1_entity *lif;
struct vsp1_entity *uds;
struct vsp1_entity *uds_input;
struct list_head entities;
};
static inline struct vsp1_pipeline *to_vsp1_pipeline(struct media_entity *e)
{
if (likely(e->pipe))
return container_of(e->pipe, struct vsp1_pipeline, pipe);
else
return NULL;
}
struct vsp1_video_buffer {
struct vb2_buffer buf;
struct list_head queue;
dma_addr_t addr[3];
unsigned int length[3];
};
static inline struct vsp1_video_buffer *
to_vsp1_video_buffer(struct vb2_buffer *vb)
{
return container_of(vb, struct vsp1_video_buffer, buf);
}
struct vsp1_video_operations {
void (*queue)(struct vsp1_video *video, struct vsp1_video_buffer *buf);
};
struct vsp1_video {
struct vsp1_device *vsp1;
struct vsp1_entity *rwpf;
const struct vsp1_video_operations *ops;
struct video_device video;
enum v4l2_buf_type type;
struct media_pad pad;
struct mutex lock;
struct v4l2_pix_format_mplane format;
const struct vsp1_format_info *fmtinfo;
struct vsp1_pipeline pipe;
unsigned int pipe_index;
struct vb2_queue queue;
void *alloc_ctx;
spinlock_t irqlock;
struct list_head irqqueue;
unsigned int sequence;
};
static inline struct vsp1_video *to_vsp1_video(struct video_device *vdev)
{
return container_of(vdev, struct vsp1_video, video);
}
int vsp1_video_init(struct vsp1_video *video, struct vsp1_entity *rwpf);
void vsp1_video_cleanup(struct vsp1_video *video);
void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe);
void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_entity *input,
unsigned int alpha);
#endif /* __VSP1_VIDEO_H__ */

View file

@ -0,0 +1,299 @@
/*
* vsp1_wpf.c -- R-Car VSP1 Write Pixel Formatter
*
* Copyright (C) 2013-2014 Renesas Electronics Corporation
*
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <media/v4l2-subdev.h>
#include "vsp1.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define WPF_MAX_WIDTH 2048
#define WPF_MAX_HEIGHT 2048
/* -----------------------------------------------------------------------------
* Device Access
*/
static inline u32 vsp1_wpf_read(struct vsp1_rwpf *wpf, u32 reg)
{
return vsp1_read(wpf->entity.vsp1,
reg + wpf->entity.index * VI6_WPF_OFFSET);
}
static inline void vsp1_wpf_write(struct vsp1_rwpf *wpf, u32 reg, u32 data)
{
vsp1_write(wpf->entity.vsp1,
reg + wpf->entity.index * VI6_WPF_OFFSET, data);
}
/* -----------------------------------------------------------------------------
* Controls
*/
static int wpf_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vsp1_rwpf *wpf =
container_of(ctrl->handler, struct vsp1_rwpf, ctrls);
u32 value;
if (!vsp1_entity_is_streaming(&wpf->entity))
return 0;
switch (ctrl->id) {
case V4L2_CID_ALPHA_COMPONENT:
value = vsp1_wpf_read(wpf, VI6_WPF_OUTFMT);
value &= ~VI6_WPF_OUTFMT_PDV_MASK;
value |= ctrl->val << VI6_WPF_OUTFMT_PDV_SHIFT;
vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, value);
break;
}
return 0;
}
static const struct v4l2_ctrl_ops wpf_ctrl_ops = {
.s_ctrl = wpf_s_ctrl,
};
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Core Operations
*/
static int wpf_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct vsp1_pipeline *pipe = to_vsp1_pipeline(&subdev->entity);
struct vsp1_rwpf *wpf = to_rwpf(subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
const struct v4l2_rect *crop = &wpf->crop;
unsigned int i;
u32 srcrpf = 0;
u32 outfmt = 0;
int ret;
ret = vsp1_entity_set_streaming(&wpf->entity, enable);
if (ret < 0)
return ret;
if (!enable) {
vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index), 0);
vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, 0);
return 0;
}
/* Sources. If the pipeline has a single input configure it as the
* master layer. Otherwise configure all inputs as sub-layers and
* select the virtual RPF as the master layer.
*/
for (i = 0; i < pipe->num_inputs; ++i) {
struct vsp1_rwpf *input = pipe->inputs[i];
srcrpf |= pipe->num_inputs == 1
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
if (pipe->num_inputs > 1)
srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST;
vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf);
/* Destination stride. */
if (!pipe->lif) {
struct v4l2_pix_format_mplane *format = &wpf->video.format;
vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_Y,
format->plane_fmt[0].bytesperline);
if (format->num_planes > 1)
vsp1_wpf_write(wpf, VI6_WPF_DSTM_STRIDE_C,
format->plane_fmt[1].bytesperline);
}
vsp1_wpf_write(wpf, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
(crop->left << VI6_WPF_SZCLIP_OFST_SHIFT) |
(crop->width << VI6_WPF_SZCLIP_SIZE_SHIFT));
vsp1_wpf_write(wpf, VI6_WPF_VSZCLIP, VI6_WPF_SZCLIP_EN |
(crop->top << VI6_WPF_SZCLIP_OFST_SHIFT) |
(crop->height << VI6_WPF_SZCLIP_SIZE_SHIFT));
/* Format */
if (!pipe->lif) {
const struct vsp1_format_info *fmtinfo = wpf->video.fmtinfo;
outfmt = fmtinfo->hwfmt << VI6_WPF_OUTFMT_WRFMT_SHIFT;
if (fmtinfo->alpha)
outfmt |= VI6_WPF_OUTFMT_PXA;
if (fmtinfo->swap_yc)
outfmt |= VI6_WPF_OUTFMT_SPYCS;
if (fmtinfo->swap_uv)
outfmt |= VI6_WPF_OUTFMT_SPUVS;
vsp1_wpf_write(wpf, VI6_WPF_DSWAP, fmtinfo->swap);
}
if (wpf->entity.formats[RWPF_PAD_SINK].code !=
wpf->entity.formats[RWPF_PAD_SOURCE].code)
outfmt |= VI6_WPF_OUTFMT_CSC;
/* Take the control handler lock to ensure that the PDV value won't be
* changed behind our back by a set control operation.
*/
mutex_lock(wpf->ctrls.lock);
outfmt |= vsp1_wpf_read(wpf, VI6_WPF_OUTFMT) & VI6_WPF_OUTFMT_PDV_MASK;
vsp1_wpf_write(wpf, VI6_WPF_OUTFMT, outfmt);
mutex_unlock(wpf->ctrls.lock);
vsp1_write(vsp1, VI6_DPR_WPF_FPORCH(wpf->entity.index),
VI6_DPR_WPF_FPORCH_FP_WPFN);
vsp1_write(vsp1, VI6_WPF_WRBCK_CTRL, 0);
/* Enable interrupts */
vsp1_write(vsp1, VI6_WPF_IRQ_STA(wpf->entity.index), 0);
vsp1_write(vsp1, VI6_WPF_IRQ_ENB(wpf->entity.index),
VI6_WFP_IRQ_ENB_FREE);
return 0;
}
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
static struct v4l2_subdev_video_ops wpf_video_ops = {
.s_stream = wpf_s_stream,
};
static struct v4l2_subdev_pad_ops wpf_pad_ops = {
.enum_mbus_code = vsp1_rwpf_enum_mbus_code,
.enum_frame_size = vsp1_rwpf_enum_frame_size,
.get_fmt = vsp1_rwpf_get_format,
.set_fmt = vsp1_rwpf_set_format,
.get_selection = vsp1_rwpf_get_selection,
.set_selection = vsp1_rwpf_set_selection,
};
static struct v4l2_subdev_ops wpf_ops = {
.video = &wpf_video_ops,
.pad = &wpf_pad_ops,
};
/* -----------------------------------------------------------------------------
* Video Device Operations
*/
static void wpf_vdev_queue(struct vsp1_video *video,
struct vsp1_video_buffer *buf)
{
struct vsp1_rwpf *wpf = container_of(video, struct vsp1_rwpf, video);
vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_Y, buf->addr[0]);
if (buf->buf.num_planes > 1)
vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C0, buf->addr[1]);
if (buf->buf.num_planes > 2)
vsp1_wpf_write(wpf, VI6_WPF_DSTM_ADDR_C1, buf->addr[2]);
}
static const struct vsp1_video_operations wpf_vdev_ops = {
.queue = wpf_vdev_queue,
};
/* -----------------------------------------------------------------------------
* Initialization and Cleanup
*/
struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index)
{
struct v4l2_subdev *subdev;
struct vsp1_video *video;
struct vsp1_rwpf *wpf;
unsigned int flags;
int ret;
wpf = devm_kzalloc(vsp1->dev, sizeof(*wpf), GFP_KERNEL);
if (wpf == NULL)
return ERR_PTR(-ENOMEM);
wpf->max_width = WPF_MAX_WIDTH;
wpf->max_height = WPF_MAX_HEIGHT;
wpf->entity.type = VSP1_ENTITY_WPF;
wpf->entity.index = index;
ret = vsp1_entity_init(vsp1, &wpf->entity, 2);
if (ret < 0)
return ERR_PTR(ret);
/* Initialize the V4L2 subdev. */
subdev = &wpf->entity.subdev;
v4l2_subdev_init(subdev, &wpf_ops);
subdev->entity.ops = &vsp1_media_ops;
subdev->internal_ops = &vsp1_subdev_internal_ops;
snprintf(subdev->name, sizeof(subdev->name), "%s wpf.%u",
dev_name(vsp1->dev), index);
v4l2_set_subdevdata(subdev, wpf);
subdev->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
vsp1_entity_init_formats(subdev, NULL);
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&wpf->ctrls, 1);
v4l2_ctrl_new_std(&wpf->ctrls, &wpf_ctrl_ops, V4L2_CID_ALPHA_COMPONENT,
0, 255, 1, 255);
wpf->entity.subdev.ctrl_handler = &wpf->ctrls;
if (wpf->ctrls.error) {
dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
index);
ret = wpf->ctrls.error;
goto error;
}
/* Initialize the video device. */
video = &wpf->video;
video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
video->vsp1 = vsp1;
video->ops = &wpf_vdev_ops;
ret = vsp1_video_init(video, &wpf->entity);
if (ret < 0)
goto error;
wpf->entity.video = video;
/* Connect the video device to the WPF. All connections are immutable
* except for the WPF0 source link if a LIF is present.
*/
flags = MEDIA_LNK_FL_ENABLED;
if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0)
flags |= MEDIA_LNK_FL_IMMUTABLE;
ret = media_entity_create_link(&wpf->entity.subdev.entity,
RWPF_PAD_SOURCE,
&wpf->video.video.entity, 0, flags);
if (ret < 0)
goto error;
wpf->entity.sink = &wpf->video.video.entity;
return wpf;
error:
vsp1_entity_destroy(&wpf->entity);
return ERR_PTR(ret);
}