1
0
Fork 0

staging: media: imx: enable ISI for imx8mn platform

ISI is image sensor interface of imx8 family. It's reused in imx8mn
platform. But they use different clock tree, so driver need to select
the related clock operation for different platform. In order to solve
the problem, driver define mxc_isi_dev_ops which used to control clock
operation.

Dispmix subsystem of imx8mn is consist of ISI, CSI, DSI and LCDIF modules.
It use GPR to manage the reset and clock signal for all modules. We add
a reset driver for dispmix reset function, so add related consumer of reset
in ISI core driver.

Signed-off-by: Guoniu.zhou <guoniu.zhou@nxp.com>
5.4-rM2-2.2.x-imx-squashed
Guoniu.zhou 2019-10-25 10:00:38 +08:00
parent d8977ba09f
commit f093fe3262
5 changed files with 336 additions and 26 deletions

View File

@ -166,7 +166,8 @@ struct mxc_isi_fmt *mxc_isi_get_src_fmt(struct v4l2_subdev_format *sd_fmt)
if (sd_fmt->format.code == MEDIA_BUS_FMT_YUYV8_1X16 ||
sd_fmt->format.code == MEDIA_BUS_FMT_YVYU8_2X8 ||
sd_fmt->format.code == MEDIA_BUS_FMT_AYUV8_1X32 ||
sd_fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8)
sd_fmt->format.code == MEDIA_BUS_FMT_UYVY8_2X8 ||
sd_fmt->format.code == MEDIA_BUS_FMT_YUYV8_2X8)
index = 1;
else
index = 0;
@ -1134,6 +1135,7 @@ static int mxc_isi_cap_enum_framesizes(struct file *file, void *priv,
struct v4l2_frmsizeenum *fsize)
{
struct mxc_isi_cap_dev *isi_cap = video_drvdata(file);
struct device_node *parent;
struct v4l2_subdev *sd;
struct mxc_isi_fmt *fmt;
struct v4l2_subdev_frame_size_enum fse = {
@ -1157,6 +1159,11 @@ static int mxc_isi_cap_enum_framesizes(struct file *file, void *priv,
if (ret)
return ret;
parent = of_get_parent(isi_cap->pdev->dev.of_node);
if ((of_device_is_compatible(parent, "fsl,imx8mn-isi")) &&
(fse.max_width > ISI_2K || fse.min_width > ISI_2K))
return -EINVAL;
if (fse.min_width == fse.max_width &&
fse.min_height == fse.max_height) {
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
@ -1180,6 +1187,7 @@ static int mxc_isi_cap_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *interval)
{
struct mxc_isi_cap_dev *isi_cap = video_drvdata(file);
struct device_node *parent;
struct v4l2_subdev *sd;
struct mxc_isi_fmt *fmt;
struct v4l2_subdev_frame_interval_enum fie = {
@ -1203,6 +1211,11 @@ static int mxc_isi_cap_enum_frameintervals(struct file *file, void *fh,
if (ret)
return ret;
parent = of_get_parent(isi_cap->pdev->dev.of_node);
if (of_device_is_compatible(parent, "fsl,imx8mn-isi") &&
fie.width > ISI_2K)
return -EINVAL;
interval->type = V4L2_FRMIVAL_TYPE_DISCRETE;
interval->discrete = fie.interval;
@ -1353,6 +1366,7 @@ static int mxc_isi_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_format *fmt)
{
struct mxc_isi_cap_dev *isi_cap = v4l2_get_subdevdata(sd);
struct device_node *parent;
struct v4l2_mbus_framefmt *mf = &fmt->format;
struct mxc_isi_frame *dst_f = &isi_cap->dst_f;
struct mxc_isi_fmt *out_fmt;
@ -1373,6 +1387,11 @@ static int mxc_isi_subdev_set_fmt(struct v4l2_subdev *sd,
return -EINVAL;
}
parent = of_get_parent(isi_cap->pdev->dev.of_node);
if (of_device_is_compatible(parent, "fsl,imx8mn-isi") &&
mf->width > ISI_2K)
return -EINVAL;
mutex_lock(&isi_cap->lock);
/* update out put frame size and formate */
dst_f->fmt = &mxc_isi_out_formats[i];

View File

@ -6,6 +6,8 @@
#include "imx8-isi-hw.h"
static const struct of_device_id mxc_isi_of_match[];
struct mxc_isi_dev *mxc_isi_get_hostdata(struct platform_device *pdev)
{
struct mxc_isi_dev *mxc_isi;
@ -55,6 +57,7 @@ static irqreturn_t mxc_isi_irq_handler(int irq, void *priv)
spin_lock(&mxc_isi->slock);
status = mxc_isi_get_irq_status(mxc_isi);
mxc_isi->status = status;
mxc_isi_clean_irq_status(mxc_isi, status);
if (status & CHNL_STS_FRM_STRD_MASK) {
@ -88,6 +91,146 @@ static irqreturn_t mxc_isi_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
static int disp_mix_sft_rstn(struct reset_control *reset, bool enable)
{
int ret;
if (!reset)
return 0;
ret = enable ? reset_control_assert(reset) :
reset_control_deassert(reset);
return ret;
}
static int disp_mix_clks_enable(struct reset_control *reset, bool enable)
{
int ret;
if (!reset)
return 0;
ret = enable ? reset_control_assert(reset) :
reset_control_deassert(reset);
return ret;
}
static int mxc_imx8_clk_get(struct mxc_isi_dev *mxc_isi)
{
struct device *dev = &mxc_isi->pdev->dev;
mxc_isi->clk = devm_clk_get(dev, NULL);
if (IS_ERR(mxc_isi->clk)) {
dev_err(dev, "failed to get isi clk\n");
return PTR_ERR(mxc_isi->clk);
}
return 0;
}
static int mxc_imx8_clk_enable(struct mxc_isi_dev *mxc_isi)
{
struct device *dev = &mxc_isi->pdev->dev;
int ret;
ret = clk_prepare_enable(mxc_isi->clk);
if (ret < 0) {
dev_err(dev, "%s, enable clk error\n", __func__);
return ret;
}
return 0;
}
static void mxc_imx8_clk_disable(struct mxc_isi_dev *mxc_isi)
{
clk_disable_unprepare(mxc_isi->clk);
}
static struct mxc_isi_dev_ops mxc_imx8_data = {
.clk_get = mxc_imx8_clk_get,
.clk_enable = mxc_imx8_clk_enable,
.clk_disable = mxc_imx8_clk_disable,
};
static int mxc_imx8mn_clk_get(struct mxc_isi_dev *mxc_isi)
{
struct device *dev = &mxc_isi->pdev->dev;
mxc_isi->clk_disp_axi = devm_clk_get(dev, "disp_axi");
if (IS_ERR(mxc_isi->clk_disp_axi)) {
dev_err(dev, "failed to get disp_axi clk\n");
return PTR_ERR(mxc_isi->clk_disp_axi);
}
mxc_isi->clk_disp_apb = devm_clk_get(dev, "disp_apb");
if (IS_ERR(mxc_isi->clk_disp_apb)) {
dev_err(dev, "failed to get disp_apb clk\n");
return PTR_ERR(mxc_isi->clk_disp_apb);
}
mxc_isi->clk_root_disp_axi = devm_clk_get(dev, "disp_axi_root");
if (IS_ERR(mxc_isi->clk_root_disp_axi)) {
dev_err(dev, "failed to get disp axi root clk\n");
return PTR_ERR(mxc_isi->clk_root_disp_axi);
}
mxc_isi->clk_root_disp_apb = devm_clk_get(dev, "disp_apb_root");
if (IS_ERR(mxc_isi->clk_root_disp_apb)) {
dev_err(dev, "failed to get disp apb root clk\n");
return PTR_ERR(mxc_isi->clk_root_disp_apb);
}
return 0;
}
static int mxc_imx8mn_clk_enable(struct mxc_isi_dev *mxc_isi)
{
struct device *dev = &mxc_isi->pdev->dev;
int ret;
ret = clk_prepare_enable(mxc_isi->clk_disp_axi);
if (ret < 0) {
dev_err(dev, "prepare and enable axi clk error\n");
return ret;
}
ret = clk_prepare_enable(mxc_isi->clk_disp_apb);
if (ret < 0) {
dev_err(dev, "prepare and enable abp clk error\n");
return ret;
}
ret = clk_prepare_enable(mxc_isi->clk_root_disp_axi);
if (ret < 0) {
dev_err(dev, "prepare and enable axi root clk error\n");
return ret;
}
ret = clk_prepare_enable(mxc_isi->clk_root_disp_apb);
if (ret < 0) {
dev_err(dev, "prepare and enable apb root clk error\n");
return ret;
}
return 0;
}
static void mxc_imx8mn_clk_disable(struct mxc_isi_dev *mxc_isi)
{
clk_disable_unprepare(mxc_isi->clk_root_disp_axi);
clk_disable_unprepare(mxc_isi->clk_root_disp_apb);
clk_disable_unprepare(mxc_isi->clk_disp_axi);
clk_disable_unprepare(mxc_isi->clk_disp_apb);
}
static struct mxc_isi_dev_ops mxc_imx8mn_data = {
.clk_get = mxc_imx8mn_clk_get,
.clk_enable = mxc_imx8mn_clk_enable,
.clk_disable = mxc_imx8mn_clk_disable,
};
static int mxc_isi_parse_dt(struct mxc_isi_dev *mxc_isi)
{
struct device *dev = &mxc_isi->pdev->dev;
@ -109,25 +252,114 @@ static int mxc_isi_parse_dt(struct mxc_isi_dev *mxc_isi)
return 0;
}
static int mxc_isi_clk_get(struct mxc_isi_dev *mxc_isi)
{
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
if (!ops && !ops->clk_get)
return -EINVAL;
return ops->clk_get(mxc_isi);
}
static int mxc_isi_clk_enable(struct mxc_isi_dev *mxc_isi)
{
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
if (!ops && !ops->clk_enable)
return -EINVAL;
return ops->clk_enable(mxc_isi);
}
static void mxc_isi_clk_disable(struct mxc_isi_dev *mxc_isi)
{
const struct mxc_isi_dev_ops *ops = mxc_isi->ops;
if (!ops && !ops->clk_disable)
return;
ops->clk_disable(mxc_isi);
}
static int mxc_isi_of_parse_resets(struct mxc_isi_dev *mxc_isi)
{
int ret;
struct device *dev = &mxc_isi->pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *parent, *child;
struct of_phandle_args args;
struct reset_control *rstc;
const char *compat;
uint32_t len, rstc_num = 0;
ret = of_parse_phandle_with_args(np, "resets", "#reset-cells",
0, &args);
if (ret)
return ret;
parent = args.np;
for_each_child_of_node(parent, child) {
compat = of_get_property(child, "compatible", NULL);
if (!compat)
continue;
rstc = of_reset_control_array_get(child, false, false, true);
if (IS_ERR(rstc))
continue;
len = strlen(compat);
if (!of_compat_cmp("isi,soft-resetn", compat, len)) {
mxc_isi->soft_resetn = rstc;
rstc_num++;
} else if (!of_compat_cmp("isi,clk-enable", compat, len)) {
mxc_isi->clk_enable = rstc;
rstc_num++;
} else {
dev_warn(dev, "invalid isi reset node: %s\n", compat);
}
}
if (!rstc_num) {
dev_err(dev, "no invalid reset control exists\n");
return -EINVAL;
}
of_node_put(parent);
return 0;
}
static int mxc_isi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mxc_isi_dev *mxc_isi;
struct resource *res;
const struct of_device_id *of_id;
int ret = 0;
mxc_isi = devm_kzalloc(dev, sizeof(*mxc_isi), GFP_KERNEL);
if (!mxc_isi)
return -ENOMEM;
mxc_isi->pdev = pdev;
of_id = of_match_node(mxc_isi_of_match, dev->of_node);
if (!of_id)
return -EINVAL;
mxc_isi->ops = of_id->data;
if (!mxc_isi->ops) {
dev_err(dev, "Can't get platform device data\n");
return -EINVAL;
}
ret = mxc_isi_parse_dt(mxc_isi);
if (ret < 0)
return ret;
if (mxc_isi->id >= MXC_ISI_MAX_DEVS || mxc_isi->id < 0) {
dev_err(dev, "Invalid driver data or device id (%d)\n", mxc_isi->id);
dev_err(dev, "Invalid driver data or device id (%d)\n",
mxc_isi->id);
return -EINVAL;
}
@ -135,16 +367,18 @@ static int mxc_isi_probe(struct platform_device *pdev)
mutex_init(&mxc_isi->lock);
atomic_set(&mxc_isi->usage_count, 0);
mxc_isi->clk = devm_clk_get(dev, NULL);
if (IS_ERR(mxc_isi->clk)) {
dev_err(dev, "failed to get isi clk\n");
return PTR_ERR(mxc_isi->clk);
if (of_device_is_compatible(dev->of_node, "fsl,imx8mn-isi")) {
ret = mxc_isi_of_parse_resets(mxc_isi);
if (ret) {
dev_warn(dev, "Can not parse reset control\n");
return ret;
}
}
ret = clk_prepare_enable(mxc_isi->clk);
ret = mxc_isi_clk_get(mxc_isi);
if (ret < 0) {
dev_err(dev, "Prepare and enable isi clk error (%d)\n", ret);
return -EINVAL;
dev_err(dev, "ISI_%d get clocks fail\n", mxc_isi->id);
return ret;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@ -154,18 +388,26 @@ static int mxc_isi_probe(struct platform_device *pdev)
return PTR_ERR(mxc_isi->regs);
}
ret = mxc_isi_clk_enable(mxc_isi);
if (ret < 0) {
dev_err(dev, "ISI_%d enable clocks fail\n", mxc_isi->id);
return ret;
}
disp_mix_sft_rstn(mxc_isi->soft_resetn, false);
disp_mix_clks_enable(mxc_isi->clk_enable, true);
mxc_isi_clean_registers(mxc_isi);
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "Failed to get IRQ resource\n");
return -ENXIO;
goto err;
}
ret = devm_request_irq(dev, res->start, mxc_isi_irq_handler,
0, dev_name(dev), mxc_isi);
if (ret < 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
return -EINVAL;
goto err;
}
mxc_isi_channel_set_chain_buf(mxc_isi);
@ -174,13 +416,19 @@ static int mxc_isi_probe(struct platform_device *pdev)
if (ret < 0)
dev_warn(dev, "Populate child platform device fail\n");
clk_disable_unprepare(mxc_isi->clk);
mxc_isi_clk_disable(mxc_isi);
platform_set_drvdata(pdev, mxc_isi);
pm_runtime_enable(dev);
dev_info(dev, "mxc_isi.%d registered successfully\n", mxc_isi->id);
return 0;
err:
disp_mix_clks_enable(mxc_isi->clk_enable, false);
disp_mix_sft_rstn(mxc_isi->soft_resetn, true);
mxc_isi_clk_disable(mxc_isi);
return -ENXIO;
}
static int mxc_isi_remove(struct platform_device *pdev)
@ -213,7 +461,9 @@ static int mxc_isi_runtime_suspend(struct device *dev)
{
struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
clk_disable_unprepare(mxc_isi->clk);
disp_mix_clks_enable(mxc_isi->clk_enable, false);
mxc_isi_clk_disable(mxc_isi);
return 0;
}
@ -222,11 +472,15 @@ static int mxc_isi_runtime_resume(struct device *dev)
struct mxc_isi_dev *mxc_isi = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(mxc_isi->clk);
if (ret)
ret = mxc_isi_clk_enable(mxc_isi);
if (ret) {
dev_err(dev, "%s clk enable fail\n", __func__);
return ret;
}
disp_mix_sft_rstn(mxc_isi->soft_resetn, false);
disp_mix_clks_enable(mxc_isi->clk_enable, true);
return (ret) ? ret : 0;
return 0;
}
static const struct dev_pm_ops mxc_isi_pm_ops = {
@ -235,7 +489,8 @@ static const struct dev_pm_ops mxc_isi_pm_ops = {
};
static const struct of_device_id mxc_isi_of_match[] = {
{.compatible = "fsl,imx8-isi",},
{.compatible = "fsl,imx8-isi", .data = &mxc_imx8_data },
{.compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, mxc_isi_of_match);

View File

@ -33,6 +33,8 @@
#include <media/v4l2-ctrls.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include "imx8-common.h"
@ -41,6 +43,8 @@
#define MXC_ISI_M2M "mxc-isi-m2m"
#define MXC_MAX_PLANES 3
struct mxc_isi_dev;
enum mxc_isi_out_fmt {
MXC_ISI_OUT_FMT_RGBA32 = 0x0,
MXC_ISI_OUT_FMT_ABGR32,
@ -188,6 +192,7 @@ struct mxc_isi_buffer {
struct vb2_v4l2_buffer v4l2_buf;
struct list_head list;
struct frame_addr paddr;
enum mxc_isi_buf_id id;
bool discard;
};
@ -223,6 +228,12 @@ struct mxc_isi_ctx {
struct v4l2_fh fh;
};
struct mxc_isi_dev_ops {
int (*clk_get)(struct mxc_isi_dev *mxc_isi);
int (*clk_enable)(struct mxc_isi_dev *mxc_isi);
void (*clk_disable)(struct mxc_isi_dev *mxc_isi);
};
struct mxc_isi_cap_dev {
struct v4l2_subdev sd;
struct video_device vdev;
@ -265,8 +276,21 @@ struct mxc_isi_dev {
struct mxc_isi_m2m_dev *isi_m2m;
struct platform_device *pdev;
/* clk for imx8qxp/qm platform */
struct clk *clk;
/* clks for imx8mn platform */
struct clk *clk_disp_axi;
struct clk *clk_disp_apb;
struct clk *clk_root_disp_axi;
struct clk *clk_root_disp_apb;
const struct mxc_isi_dev_ops *ops;
struct reset_control *soft_resetn;
struct reset_control *clk_enable;
struct mutex lock;
spinlock_t slock;
@ -285,6 +309,8 @@ struct mxc_isi_dev {
u32 pre_dec_x;
u32 pre_dec_y;
u32 status;
u32 interface[MAX_PORTS];
int id;

View File

@ -101,12 +101,13 @@ static void printk_pixelformat(char *prefix, int val)
static bool is_rgb(u32 pix_fmt)
{
if ((pix_fmt == V4L2_PIX_FMT_RGB565) ||
(pix_fmt == V4L2_PIX_FMT_RGB24) ||
(pix_fmt == V4L2_PIX_FMT_RGB32) ||
(pix_fmt == V4L2_PIX_FMT_BGR32) ||
(pix_fmt == V4L2_PIX_FMT_RGB24) ||
(pix_fmt == V4L2_PIX_FMT_RGB32) ||
(pix_fmt == V4L2_PIX_FMT_BGR32) ||
(pix_fmt == V4L2_PIX_FMT_XRGB32) ||
(pix_fmt == V4L2_PIX_FMT_XBGR32) ||
(pix_fmt == V4L2_PIX_FMT_BGR24) ||
(pix_fmt == V4L2_PIX_FMT_BGR24) ||
(pix_fmt == V4L2_PIX_FMT_RGBA) ||
(pix_fmt == V4L2_PIX_FMT_ABGR32) ||
(pix_fmt == V4L2_PIX_FMT_ARGB32))
return true;
@ -116,9 +117,10 @@ static bool is_rgb(u32 pix_fmt)
static bool is_yuv(u32 pix_fmt)
{
if ((pix_fmt == V4L2_PIX_FMT_YUYV) ||
if ((pix_fmt == V4L2_PIX_FMT_YUYV) ||
(pix_fmt == V4L2_PIX_FMT_YUV32) ||
(pix_fmt == V4L2_PIX_FMT_YUV444M) ||
(pix_fmt == V4L2_PIX_FMT_YUV24) ||
(pix_fmt == V4L2_PIX_FMT_NV12))
return true;
else
@ -129,7 +131,7 @@ static void chain_buf(struct mxc_isi_dev *mxc_isi, struct mxc_isi_frame *frm)
{
u32 val;
if (frm->o_width > 2048) {
if (frm->o_width > ISI_2K) {
val = readl(mxc_isi->regs + CHNL_CTRL);
val &= ~CHNL_CTRL_CHAIN_BUF_MASK;
val |= (CHNL_CTRL_CHAIN_BUF_2_CHAIN << CHNL_CTRL_CHAIN_BUF_OFFSET);
@ -174,16 +176,18 @@ void mxc_isi_channel_set_outbuf(struct mxc_isi_dev *mxc_isi,
val = readl(mxc_isi->regs + CHNL_OUT_BUF_CTRL);
if (framecount % 2 == 0) {
if (framecount == 0 || ((mxc_isi->status & 0x100) && (framecount != 1))) {
writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_Y);
writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_U);
writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF1_ADDR_V);
val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF1_ADDR_MASK;
} else if (framecount % 2 == 1) {
buf->id = MXC_ISI_BUF1;
} else if (framecount == 1 || mxc_isi->status & 0x200) {
writel(paddr->y, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_Y);
writel(paddr->cb, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_U);
writel(paddr->cr, mxc_isi->regs + CHNL_OUT_BUF2_ADDR_V);
val ^= CHNL_OUT_BUF_CTRL_LOAD_BUF2_ADDR_MASK;
buf->id = MXC_ISI_BUF2;
}
writel(val, mxc_isi->regs + CHNL_OUT_BUF_CTRL);
}
@ -701,7 +705,6 @@ void mxc_isi_m2m_config_src(struct mxc_isi_dev *mxc_isi,
void mxc_isi_m2m_config_dst(struct mxc_isi_dev *mxc_isi,
struct mxc_isi_frame *dst_f)
{
/*struct mxc_isi_frame *dst_f = &mxc_isi->isi_m2m->dst_f;*/
u32 val;
/* out format */

View File

@ -447,6 +447,13 @@
#define CHNL_SCL_IMG_CFG_WIDTH_OFFSET 0
#define CHNL_SCL_IMG_CFG_WIDTH_MASK 0x1FFF
/* Channel Flow Control Register */
#define CHNL_FLOW_CTRL 0x9C
#define CHNL_FLOW_CTRL_FC_DENOM_MASK 0xFF
#define CHNL_FLOW_CTRL_FC_DENOM_OFFSET 0
#define CHNL_FLOW_CTRL_FC_NUMER_MASK 0xFF0000
#define CHNL_FLOW_CTRL_FC_NUMER_OFFSET 0
enum isi_csi_coeff {
YUV2RGB = 0,
RGB2YUV,