// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Renesas R-Car VIN
*
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
*
* Based on the soc-camera rcar_vin driver
*/
#include <linux/pm_runtime.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-rect.h>
#include "rcar-vin.h"
#define RVIN_DEFAULT_FORMAT V4L2_PIX_FMT_YUYV
#define RVIN_DEFAULT_WIDTH 800
#define RVIN_DEFAULT_HEIGHT 600
#define RVIN_DEFAULT_FIELD V4L2_FIELD_NONE
#define RVIN_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB
/* -----------------------------------------------------------------------------
* Format Conversions
*/
static const struct rvin_video_format rvin_formats[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
.bpp = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV16,
.bpp = 1,
},
{
.fourcc = V4L2_PIX_FMT_YUYV,
.bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_UYVY,
.bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_RGB565,
.bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_XRGB555,
.bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_XBGR32,
.bpp = 4,
},
{
.fourcc = V4L2_PIX_FMT_ARGB555,
.bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_ABGR32,
.bpp = 4,
},
{
.fourcc = V4L2_PIX_FMT_SBGGR8,
.bpp = 1,
},
{
.fourcc = V4L2_PIX_FMT_SGBRG8,
.bpp = 1,
},
{
.fourcc = V4L2_PIX_FMT_SGRBG8,
.bpp = 1,
},
{
.fourcc = V4L2_PIX_FMT_SRGGB8,
.bpp = 1,
},
};
const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
u32 pixelformat)
{
int i;
switch (pixelformat) {
case V4L2_PIX_FMT_XBGR32:
if (vin->info->model == RCAR_M1)
return NULL;
break;
case V4L2_PIX_FMT_NV12:
/*
* If NV12 is supported it's only supported on channels 0, 1, 4,
* 5, 8, 9, 12 and 13.
*/
if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333))
return NULL;
break;
default:
break;
}
for (i = 0; i < ARRAY_SIZE(rvin_formats); i++)
if (rvin_formats[i].fourcc == pixelformat)
return rvin_formats + i;
return NULL;
}
static u32 rvin_format_bytesperline(struct rvin_dev *vin,
struct v4l2_pix_format *pix)
{
const struct rvin_video_format *fmt;
u32 align;
fmt = rvin_format_from_pixel(vin, pix->pixelformat);
if (WARN_ON(!fmt))
return -EINVAL;
switch (pix->pixelformat) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV16:
align = 0x20;
break;
default:
align = 0x10;
break;
}
if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
align = 0x80;
return ALIGN(pix->width, align) * fmt->bpp;
}
static u32 rvin_format_sizeimage(struct v4l2_pix_format *pix)
{
switch (pix->pixelformat) {
case V4L2_PIX_FMT_NV12:
return pix->bytesperline * pix->height * 3 / 2;
case V4L2_PIX_FMT_NV16:
return pix->bytesperline * pix->height * 2;
default:
return pix->bytesperline * pix->height;
}
}
static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
{
u32 walign;
if (!rvin_format_from_pixel(vin, pix->pixelformat))
pix->pixelformat = RVIN_DEFAULT_FORMAT;
switch (pix->field) {
case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTO