// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
* All rights reserved.
* Author: Yong Deng <yong.deng@magewell.com>
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/sched.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include "sun6i_csi.h"
#include "sun6i_csi_reg.h"
#define MODULE_NAME "sun6i-csi"
struct sun6i_csi_dev {
struct sun6i_csi csi;
struct device *dev;
struct regmap *regmap;
struct clk *clk_mod;
struct clk *clk_ram;
struct reset_control *rstc_bus;
int planar_offset[3];
};
static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
{
return container_of(csi, struct sun6i_csi_dev, csi);
}
/* TODO add 10&12 bit YUV, RGB support */
bool sun6i_csi_is_format_supported(struct sun6i_csi *csi,
u32 pixformat, u32 mbus_code)
{
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
/*
* Some video receivers have the ability to be compatible with
* 8bit and 16bit bus width.
* Identify the media bus format from device tree.
*/
if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
|| sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
&& sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
switch (pixformat) {
case V4L2_PIX_FMT_HM12:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV422P:
switch (mbus_code) {
case MEDIA_BUS_FMT_UYVY8_1X16:
case MEDIA_BUS_FMT_VYUY8_1X16:
case MEDIA_BUS_FMT_YUYV8_1X16:
case MEDIA_BUS_FMT_YVYU8_1X16:
return true;
default:
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
mbus_code);
break;
}
break;
default:
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n",
pixformat);
break;
}
return false;
}
switch (pixformat) {
case V4L2_PIX_FMT_SBGGR8:
return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
case V4L2_PIX_FMT_SGBRG8:
return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
case V4L2_PIX_FMT_SGRBG8:
return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
case V4L2_PIX_FMT_SRGGB8:
return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
case V4L2_PIX_FMT_SBGGR10:
return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
case V4L2_PIX_FMT_SGBRG10:
return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
case V4L2_PIX_FMT_SGRBG10:
return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
case V4L2_PIX_FMT_SRGGB10:
return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
case V4L2_PIX_FMT_SBGGR12:
return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
case V4L2_PIX_FMT_SGBRG12:
return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
case V4L2_PIX_FMT_SGRBG12:
return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
case V4L2_PIX_FMT_SRGGB12:
return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
case V4L2_PIX_FMT_YUYV:
return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
case V4L2_PIX_FMT_YVYU:
return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
case V4L2_PIX_FMT_UYVY:
return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
case V4L2_PIX_FMT_VYUY:
return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
case V4L2_PIX_FMT_HM12:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV422P:
switch (mbus_code) {
case MEDIA_BUS_FMT_UYVY8_2X8:
case MEDIA_BUS_FMT_VYUY8_2X8:
case MEDIA_BUS_FMT_YUYV8_2X8:
case MEDIA_BUS_FMT_YVYU8_2X8:
return true;
default:
dev_dbg(sdev->dev, "Unsupported mbus code: 0x%x\n",
mbus_code);
break;
}
break;
default:
dev_dbg(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
break;
}
return false;
}
int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
{
struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
struct device *dev = sdev->dev;
struct regmap *regmap = sdev->regmap;
int ret;
if (!enable) {
regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
clk_disable_unprepare(sdev->clk_ram);
if (of_device_is_compatible(dev->of_node,
"allwinner,sun50i-a64-csi&qu