fbdev: dcic: Enable imx6 dcic driver
This patch forwards imx6 dcic driver from imx_4.19.y kernel. Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
This commit is contained in:
parent
46dabba394
commit
ce69b9fef5
|
@ -115,3 +115,9 @@ config FB_MXC_HDMI
|
|||
config FB_MXS_SII902X
|
||||
tristate "Si Image SII9022 DVI/HDMI Interface Chip"
|
||||
depends on FB_MXS && I2C
|
||||
|
||||
config FB_MXC_DCIC
|
||||
tristate "MXC DCIC"
|
||||
depends on FB_MXC_SYNC_PANEL
|
||||
depends on MXC_IPU_V3 || FB_MXS
|
||||
select VIDEOMODE_HELPERS
|
||||
|
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_FB_MXC_MIPI_DSI) += mipi_dsi.o
|
|||
obj-$(CONFIG_FB_MXC_LDB) += ldb.o
|
||||
obj-$(CONFIG_FB_MXS_SII902X) += mxsfb_sii902x.o mxsfb_sii902x_audio.o
|
||||
obj-$(CONFIG_FB_MXC_HDMI) += mxc_hdmi.o
|
||||
obj-$(CONFIG_FB_MXC_DCIC) += mxc_dcic.o
|
||||
obj-$(CONFIG_FB_MXC_SYNC_PANEL) += mxc_ipuv3_fb.o
|
||||
obj-$(CONFIG_FB_MXC_EINK_PANEL) += mxc_epdc_fb.o
|
||||
obj-$(CONFIG_FB_MXC_EINK_V2_PANEL) += mxc_epdc_v2_fb.o
|
||||
|
|
|
@ -0,0 +1,593 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mxc_dcic.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <video/videomode.h>
|
||||
#include <video/of_videomode.h>
|
||||
|
||||
#define DRIVER_NAME "mxc_dcic"
|
||||
|
||||
#define DCIC_IPU1_DI0 "dcic-ipu1-di0"
|
||||
#define DCIC_IPU1_DI1 "dcic-ipu1-di1"
|
||||
#define DCIC_IPU2_DI0 "dcic-ipu2-di0"
|
||||
#define DCIC_IPU2_DI1 "dcic-ipu2-di1"
|
||||
#define DCIC_LCDIF "dcic-lcdif"
|
||||
#define DCIC_LCDIF1 "dcic-lcdif1"
|
||||
#define DCIC_LCDIF2 "dcic-lcdif2"
|
||||
#define DCIC_LVDS "dcic-lvds"
|
||||
#define DCIC_LVDS0 "dcic-lvds0"
|
||||
#define DCIC_LVDS1 "dcic-lvds1"
|
||||
#define DCIC_HDMI "dcic-hdmi"
|
||||
|
||||
#define DCIC0_DEV_NAME "mxc_dcic0"
|
||||
#define DCIC1_DEV_NAME "mxc_dcic1"
|
||||
|
||||
#define FB_SYNC_OE_LOW_ACT 0x80000000
|
||||
#define FB_SYNC_CLK_LAT_FALL 0x40000000
|
||||
|
||||
static const struct dcic_mux imx6q_dcic0_mux[] = {
|
||||
{
|
||||
.dcic = DCIC_IPU1_DI0,
|
||||
.val = IMX6Q_GPR10_DCIC1_MUX_CTL_IPU1_DI0,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS0,
|
||||
.val = IMX6Q_GPR10_DCIC1_MUX_CTL_LVDS0,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS1,
|
||||
.val = IMX6Q_GPR10_DCIC1_MUX_CTL_LVDS1,
|
||||
}, {
|
||||
.dcic = DCIC_HDMI,
|
||||
.val = IMX6Q_GPR10_DCIC1_MUX_CTL_HDMI,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dcic_mux imx6q_dcic1_mux[] = {
|
||||
{
|
||||
.dcic = DCIC_IPU1_DI1,
|
||||
.val = IMX6Q_GPR10_DCIC2_MUX_CTL_IPU1_DI1,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS0,
|
||||
.val = IMX6Q_GPR10_DCIC2_MUX_CTL_LVDS0,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS1,
|
||||
.val = IMX6Q_GPR10_DCIC2_MUX_CTL_LVDS1,
|
||||
}, {
|
||||
.dcic = DCIC_HDMI,
|
||||
.val = IMX6Q_GPR10_DCIC2_MUX_CTL_MIPI,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct bus_mux imx6q_dcic_buses[] = {
|
||||
{
|
||||
.name = DCIC0_DEV_NAME,
|
||||
.reg = IOMUXC_GPR10,
|
||||
.shift = 0,
|
||||
.mask = IMX6Q_GPR10_DCIC1_MUX_CTL_MASK,
|
||||
.dcic_mux_num = ARRAY_SIZE(imx6q_dcic0_mux),
|
||||
.dcics = imx6q_dcic0_mux,
|
||||
}, {
|
||||
.name = DCIC1_DEV_NAME,
|
||||
.reg = IOMUXC_GPR10,
|
||||
.shift = 2,
|
||||
.mask = IMX6Q_GPR10_DCIC2_MUX_CTL_MASK,
|
||||
.dcic_mux_num = ARRAY_SIZE(imx6q_dcic1_mux),
|
||||
.dcics = imx6q_dcic1_mux,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dcic_info imx6q_dcic_info = {
|
||||
.bus_mux_num = ARRAY_SIZE(imx6q_dcic_buses),
|
||||
.buses = imx6q_dcic_buses,
|
||||
};
|
||||
|
||||
static const struct dcic_mux imx6sx_dcic0_mux[] = {
|
||||
{
|
||||
.dcic = DCIC_LCDIF1,
|
||||
.val = IMX6SX_GPR5_DISP_MUX_DCIC1_LCDIF1,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS,
|
||||
.val = IMX6SX_GPR5_DISP_MUX_DCIC1_LVDS,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dcic_mux imx6sx_dcic1_mux[] = {
|
||||
{
|
||||
.dcic = DCIC_LCDIF2,
|
||||
.val = IMX6SX_GPR5_DISP_MUX_DCIC2_LCDIF2,
|
||||
}, {
|
||||
.dcic = DCIC_LVDS,
|
||||
.val = IMX6SX_GPR5_DISP_MUX_DCIC2_LVDS,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct bus_mux imx6sx_dcic_buses[] = {
|
||||
{
|
||||
.name = DCIC0_DEV_NAME,
|
||||
.reg = IOMUXC_GPR5,
|
||||
.shift = 1,
|
||||
.mask = IMX6SX_GPR5_DISP_MUX_DCIC1_MASK,
|
||||
.dcic_mux_num = ARRAY_SIZE(imx6sx_dcic0_mux),
|
||||
.dcics = imx6sx_dcic0_mux,
|
||||
}, {
|
||||
.name = DCIC1_DEV_NAME,
|
||||
.reg = IOMUXC_GPR5,
|
||||
.shift = 2,
|
||||
.mask = IMX6SX_GPR5_DISP_MUX_DCIC2_MASK,
|
||||
.dcic_mux_num = ARRAY_SIZE(imx6sx_dcic1_mux),
|
||||
.dcics = imx6sx_dcic1_mux,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct dcic_info imx6sx_dcic_info = {
|
||||
.bus_mux_num = ARRAY_SIZE(imx6sx_dcic_buses),
|
||||
.buses = imx6sx_dcic_buses,
|
||||
};
|
||||
|
||||
static const struct of_device_id dcic_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-dcic", .data = &imx6q_dcic_info, },
|
||||
{ .compatible = "fsl,imx6sx-dcic", .data = &imx6sx_dcic_info, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dcic_dt_ids);
|
||||
|
||||
static int of_get_dcic_val(struct device_node *np, struct dcic_data *dcic)
|
||||
{
|
||||
const char *mux;
|
||||
int ret;
|
||||
u32 i, dcic_id;
|
||||
|
||||
ret = of_property_read_string(np, "dcic_mux", &mux);
|
||||
if (ret < 0) {
|
||||
dev_err(dcic->dev, "Can not get dcic_mux\n");
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32(np, "dcic_id", &dcic_id);
|
||||
if (ret < 0) {
|
||||
dev_err(dcic->dev, "Can not get dcic_id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dcic->bus_n = dcic_id;
|
||||
|
||||
for (i = 0; i < dcic->buses[dcic_id].dcic_mux_num; i++)
|
||||
if (!strcmp(mux, dcic->buses[dcic_id].dcics[i].dcic)) {
|
||||
dcic->mux_n = i;
|
||||
return dcic->buses[dcic_id].dcics[i].val;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void dcic_enable(struct dcic_data *dcic)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(&dcic->regs->dcicc);
|
||||
val |= DCICC_IC_ENABLE;
|
||||
writel(val, &dcic->regs->dcicc);
|
||||
}
|
||||
|
||||
void dcic_disable(struct dcic_data *dcic)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(&dcic->regs->dcicc);
|
||||
val &= ~DCICC_IC_MASK;
|
||||
val |= DCICC_IC_DISABLE;
|
||||
writel(val, &dcic->regs->dcicc);
|
||||
}
|
||||
|
||||
static void roi_enable(struct dcic_data *dcic, struct roi_params *roi_param)
|
||||
{
|
||||
u32 val;
|
||||
u32 roi_n = roi_param->roi_n;
|
||||
|
||||
val = readl(&dcic->regs->ROI[roi_n].dcicrc);
|
||||
val |= DCICRC_ROI_ENABLE;
|
||||
if (roi_param->freeze)
|
||||
val |= DCICRC_ROI_FROZEN;
|
||||
writel(val, &dcic->regs->ROI[roi_n].dcicrc);
|
||||
}
|
||||
|
||||
static void roi_disable(struct dcic_data *dcic, u32 roi_n)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(&dcic->regs->ROI[roi_n].dcicrc);
|
||||
val &= ~DCICRC_ROI_ENABLE;
|
||||
writel(val, &dcic->regs->ROI[roi_n].dcicrc);
|
||||
}
|
||||
|
||||
static bool roi_configure(struct dcic_data *dcic, struct roi_params *roi_param)
|
||||
{
|
||||
struct roi_regs *roi_reg;
|
||||
u32 val;
|
||||
|
||||
if (roi_param->roi_n >= 16) {
|
||||
printk(KERN_ERR "Error, Wrong ROI number %d\n", roi_param->roi_n);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (roi_param->end_x <= roi_param->start_x ||
|
||||
roi_param->end_y <= roi_param->start_y) {
|
||||
printk(KERN_ERR "Error, Wrong ROI\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
roi_reg = (struct roi_regs *) &dcic->regs->ROI[roi_param->roi_n];
|
||||
|
||||
/* init roi block size */
|
||||
val = roi_param->start_y << 16 | roi_param->start_x;
|
||||
writel(val, &roi_reg->dcicrc);
|
||||
|
||||
val = roi_param->end_y << 16 | roi_param->end_x;
|
||||
writel(val, &roi_reg->dcicrs);
|
||||
|
||||
writel(roi_param->ref_sig, &roi_reg->dcicrrs);
|
||||
|
||||
roi_enable(dcic, roi_param);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dcic_int_enable(struct dcic_data *dcic)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Clean pending interrupt before enable int */
|
||||
writel(DCICS_FI_STAT_PENDING, &dcic->regs->dcics);
|
||||
writel(0xffffffff, &dcic->regs->dcics);
|
||||
|
||||
/* Enable function interrupt */
|
||||
val = readl(&dcic->regs->dcicic);
|
||||
val &= ~DCICIC_FUN_INT_MASK;
|
||||
val |= DCICIC_FUN_INT_ENABLE;
|
||||
writel(val, &dcic->regs->dcicic);
|
||||
}
|
||||
|
||||
static void dcic_int_disable(struct dcic_data *dcic)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Disable both function and error interrupt */
|
||||
val = readl(&dcic->regs->dcicic);
|
||||
val = DCICIC_ERROR_INT_DISABLE | DCICIC_FUN_INT_DISABLE;
|
||||
writel(val, &dcic->regs->dcicic);
|
||||
}
|
||||
|
||||
static irqreturn_t dcic_irq_handler(int irq, void *data)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
struct dcic_data *dcic = data;
|
||||
u32 dcics = readl(&dcic->regs->dcics);
|
||||
|
||||
dcic->result = dcics & 0xffff;
|
||||
|
||||
dcic_int_disable(dcic);
|
||||
|
||||
/* clean dcic interrupt state */
|
||||
writel(DCICS_FI_STAT_PENDING, &dcic->regs->dcics);
|
||||
writel(dcics, &dcic->regs->dcics);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
printk(KERN_INFO "ROI=%d,crcRS=0x%x, crcCS=0x%x\n", i,
|
||||
readl(&dcic->regs->ROI[i].dcicrrs),
|
||||
readl(&dcic->regs->ROI[i].dcicrcs));
|
||||
}
|
||||
complete(&dcic->roi_crc_comp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcic_configure(struct dcic_data *dcic, unsigned int sync)
|
||||
{
|
||||
u32 val;
|
||||
val = 0;
|
||||
|
||||
/* vsync, hsync, DE, clk_pol */
|
||||
if (!(sync & FB_SYNC_HOR_HIGH_ACT))
|
||||
val |= DCICC_HSYNC_POL_ACTIVE_LOW;
|
||||
if (!(sync & FB_SYNC_VERT_HIGH_ACT))
|
||||
val |= DCICC_VSYNC_POL_ACTIVE_LOW;
|
||||
if (sync & FB_SYNC_OE_LOW_ACT)
|
||||
val |= DCICC_DE_ACTIVE_LOW;
|
||||
if (sync & FB_SYNC_CLK_LAT_FALL)
|
||||
val |= DCICC_CLK_POL_INVERTED;
|
||||
|
||||
writel(val, &dcic->regs->dcicc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcic_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dcic_data *dcic;
|
||||
|
||||
dcic = container_of(inode->i_cdev, struct dcic_data, cdev);
|
||||
|
||||
mutex_lock(&dcic->lock);
|
||||
|
||||
clk_prepare_enable(dcic->disp_axi_clk);
|
||||
clk_prepare_enable(dcic->dcic_clk);
|
||||
|
||||
file->private_data = dcic;
|
||||
mutex_unlock(&dcic->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcic_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dcic_data *dcic = file->private_data;
|
||||
u32 i;
|
||||
|
||||
mutex_lock(&dcic->lock);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
roi_disable(dcic, i);
|
||||
|
||||
clk_disable_unprepare(dcic->dcic_clk);
|
||||
clk_disable_unprepare(dcic->disp_axi_clk);
|
||||
|
||||
mutex_unlock(&dcic->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dcic_init(struct device_node *np, struct dcic_data *dcic)
|
||||
{
|
||||
int val;
|
||||
u32 bus;
|
||||
|
||||
val = of_get_dcic_val(np, dcic);
|
||||
if (val < 0) {
|
||||
printk(KERN_ERR "Error incorrect\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bus = dcic->bus_n;
|
||||
|
||||
regmap_update_bits(dcic->regmap, dcic->buses[bus].reg,
|
||||
dcic->buses[bus].mask, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dcic_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int __user *argp = (void __user *)arg;
|
||||
struct dcic_data *dcic = file->private_data;
|
||||
struct roi_params roi_param;
|
||||
unsigned int sync;
|
||||
int ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case DCIC_IOC_CONFIG_DCIC:
|
||||
if (!copy_from_user(&sync, argp, sizeof(unsigned int)))
|
||||
dcic_configure(dcic, sync);
|
||||
break;
|
||||
case DCIC_IOC_CONFIG_ROI:
|
||||
if (copy_from_user(&roi_param, argp, sizeof(roi_param)))
|
||||
return -EFAULT;
|
||||
else
|
||||
if (!roi_configure(dcic, &roi_param))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case DCIC_IOC_GET_RESULT:
|
||||
init_completion(&dcic->roi_crc_comp);
|
||||
|
||||
dcic_enable(dcic);
|
||||
|
||||
dcic->result = 0;
|
||||
msleep(25);
|
||||
|
||||
dcic_int_enable(dcic);
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&dcic->roi_crc_comp, 1 * HZ);
|
||||
if (ret == 0) {
|
||||
dev_err(dcic->dev,
|
||||
"dcic wait for roi crc cal timeout\n");
|
||||
ret = -ETIME;
|
||||
} else if (ret > 0) {
|
||||
if (copy_to_user(argp, &dcic->result, sizeof(dcic->result)))
|
||||
return -EFAULT;
|
||||
ret = 0;
|
||||
}
|
||||
dcic_disable(dcic);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR "%s, Unsupport cmd %d\n", __func__, cmd);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations mxc_dcic_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = dcic_open,
|
||||
.release = dcic_release,
|
||||
.unlocked_ioctl = dcic_ioctl,
|
||||
};
|
||||
|
||||
static int dcic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(dcic_dt_ids, dev);
|
||||
const struct dcic_info *dcic_info =
|
||||
(const struct dcic_info *)of_id->data;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct dcic_data *dcic;
|
||||
struct resource *res;
|
||||
const char *name;
|
||||
dev_t devt;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
|
||||
dcic = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct dcic_data),
|
||||
GFP_KERNEL);
|
||||
if (!dcic) {
|
||||
dev_err(&pdev->dev, "Cannot allocate device data\n");
|
||||
ret = -ENOMEM;
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dcic);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "No dcic base address found.\n");
|
||||
ret = -ENODEV;
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
dcic->regs = (struct dcic_regs *) devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!dcic->regs) {
|
||||
dev_err(&pdev->dev, "ioremap failed with dcic base\n");
|
||||
ret = -ENOMEM;
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
dcic->dev = dev;
|
||||
dcic->buses = dcic_info->buses;
|
||||
|
||||
dcic->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
|
||||
if (IS_ERR(dcic->regmap)) {
|
||||
dev_err(dev, "failed to get parent regmap\n");
|
||||
ret = PTR_ERR(dcic->regmap);
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
/* clock */
|
||||
dcic->disp_axi_clk = devm_clk_get(&pdev->dev, "disp-axi");
|
||||
if (IS_ERR(dcic->disp_axi_clk)) {
|
||||
dev_err(&pdev->dev, "get disp-axi clock failed\n");
|
||||
ret = PTR_ERR(dcic->disp_axi_clk);
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
dcic->dcic_clk = devm_clk_get(&pdev->dev, "dcic");
|
||||
if (IS_ERR(dcic->dcic_clk)) {
|
||||
dev_err(&pdev->dev, "get dcic clk failed\n");
|
||||
ret = PTR_ERR(dcic->dcic_clk);
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
mutex_init(&dcic->lock);
|
||||
ret = dcic_init(np, dcic);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Failed init dcic\n");
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
/* register device */
|
||||
name = dcic->buses[dcic->bus_n].name;
|
||||
dcic->major = register_chrdev(0, name, &mxc_dcic_fops);
|
||||
if (dcic->major < 0) {
|
||||
printk(KERN_ERR "DCIC: unable to get a major for dcic\n");
|
||||
ret = -EBUSY;
|
||||
goto ealloc;
|
||||
}
|
||||
|
||||
dcic->class = class_create(THIS_MODULE, name);
|
||||
if (IS_ERR(dcic->class)) {
|
||||
ret = PTR_ERR(dcic->class);
|
||||
goto err_out_chrdev;
|
||||
}
|
||||
|
||||
/* create char device */
|
||||
devt = MKDEV(dcic->major, 0);
|
||||
dcic->devt = devt;
|
||||
|
||||
cdev_init(&dcic->cdev, &mxc_dcic_fops);
|
||||
dcic->cdev.owner = THIS_MODULE;
|
||||
ret = cdev_add(&dcic->cdev, devt, 1);
|
||||
if (ret)
|
||||
goto err_out_class;
|
||||
|
||||
device_create(dcic->class, NULL, devt,
|
||||
NULL, name);
|
||||
|
||||
/* IRQ */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, dcic_irq_handler, 0,
|
||||
dev_name(&pdev->dev), dcic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq (%d) failed with error %d\n",
|
||||
irq, ret);
|
||||
goto err_out_cdev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_cdev:
|
||||
cdev_del(&dcic->cdev);
|
||||
err_out_class:
|
||||
device_destroy(dcic->class, devt);
|
||||
class_destroy(dcic->class);
|
||||
err_out_chrdev:
|
||||
unregister_chrdev(dcic->major, name);
|
||||
ealloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dcic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dcic_data *dcic = platform_get_drvdata(pdev);
|
||||
const char *name;
|
||||
|
||||
name = dcic->buses[dcic->bus_n].name;
|
||||
|
||||
device_destroy(dcic->class, dcic->devt);
|
||||
cdev_del(&dcic->cdev);
|
||||
class_destroy(dcic->class);
|
||||
unregister_chrdev(dcic->major, name);
|
||||
mutex_destroy(&dcic->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dcic_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = dcic_dt_ids,
|
||||
},
|
||||
.probe = dcic_probe,
|
||||
.remove = dcic_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(dcic_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("MXC DCIC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
|
@ -285,15 +285,15 @@
|
|||
#define IMX6Q_GPR10_OCRAM_TZ_ADDR_MASK (0x3f << 5)
|
||||
#define IMX6Q_GPR10_OCRAM_TZ_EN_MASK BIT(4)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_MASK (0x3 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_IPU1_DI0 (0x0 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_IPU1_DI1 (0x1 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_IPU2_DI0 (0x2 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_IPU2_DI1 (0x3 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_IPU1_DI1 (0x0 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_LVDS0 (0x1 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_LVDS1 (0x2 << 2)
|
||||
#define IMX6Q_GPR10_DCIC2_MUX_CTL_MIPI (0x3 << 2)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_MASK (0x3 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_IPU1_DI0 (0x0 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_IPU1_DI1 (0x1 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_IPU2_DI0 (0x2 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_IPU2_DI1 (0x3 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_LVDS0 (0x1 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_LVDS1 (0x2 << 0)
|
||||
#define IMX6Q_GPR10_DCIC1_MUX_CTL_HDMI (0x3 << 0)
|
||||
|
||||
#define IMX6Q_GPR12_ARMP_IPG_CLK_EN BIT(27)
|
||||
#define IMX6Q_GPR12_ARMP_AHB_CLK_EN BIT(26)
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2014-2015 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
/*!
|
||||
* @file linux/mxc_dcic.h
|
||||
*
|
||||
* @brief Global header file for the MXC DCIC driver
|
||||
*
|
||||
* @ingroup MXC DCIC
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_DCIC_H__
|
||||
#define __LINUX_DCIC_H__
|
||||
|
||||
#include <uapi/linux/mxc_dcic.h>
|
||||
|
||||
#define DCICC_IC_ENABLE 0x1
|
||||
#define DCICC_IC_DISABLE 0x0
|
||||
#define DCICC_IC_MASK 0x1
|
||||
#define DCICC_DE_ACTIVE_HIGH 0
|
||||
#define DCICC_DE_ACTIVE_LOW (0x1 << 4)
|
||||
#define DCICC_DE_ACTIVE_MASK (0x1 << 4)
|
||||
#define DCICC_HSYNC_POL_ACTIVE_HIGH 0
|
||||
#define DCICC_HSYNC_POL_ACTIVE_LOW (0x1 << 5)
|
||||
#define DCICC_HSYNC_POL_ACTIVE_MASK (0x1 << 5)
|
||||
#define DCICC_VSYNC_POL_ACTIVE_HIGH 0
|
||||
#define DCICC_VSYNC_POL_ACTIVE_LOW (0x1 << 6)
|
||||
#define DCICC_VSYNC_POL_ACTIVE_MASK (0x1 << 6)
|
||||
#define DCICC_CLK_POL_NO_INVERTED 0
|
||||
#define DCICC_CLK_POL_INVERTED (0x1 << 7)
|
||||
#define DCICC_CLK_POL_INVERTED_MASK (0x1 << 7)
|
||||
|
||||
#define DCICIC_ERROR_INT_DISABLE 1
|
||||
#define DCICIC_ERROR_INT_ENABLE 0
|
||||
#define DCICIC_ERROR_INT_MASK_MASK 1
|
||||
#define DCICIC_FUN_INT_DISABLE (0x1 << 1)
|
||||
#define DCICIC_FUN_INT_ENABLE 0
|
||||
#define DCICIC_FUN_INT_MASK (0x1 << 1)
|
||||
#define DCICIC_FREEZE_MASK_CHANGED 0
|
||||
#define DCICIC_FREEZE_MASK_FORZEN (0x1 << 3)
|
||||
#define DCICIC_FREEZE_MASK_MASK (0x1 << 3)
|
||||
#define DCICIC_EXT_SIG_EX_DISABLE 0
|
||||
#define DCICIC_EXT_SIG_EN_ENABLE (0x1 << 16)
|
||||
#define DCICIC_EXT_SIG_EN_MASK (0x1 << 16)
|
||||
|
||||
#define DCICS_ROI_MATCH_STAT_MASK 0xFFFF
|
||||
#define DCICS_EI_STAT_PENDING (0x1 << 16)
|
||||
#define DCICS_EI_STAT_NO_PENDING 0
|
||||
#define DCICS_FI_STAT_PENDING (0x1 << 17)
|
||||
#define DCICS_FI_STAT_NO_PENDING 0
|
||||
|
||||
#define DCICRC_ROI_START_OFFSET_X_MASK 0x1FFF
|
||||
#define DCICRC_ROI_START_OFFSET_X_SHIFT 0
|
||||
#define DCICRC_ROI_START_OFFSET_Y_MASK (0xFFF << 16)
|
||||
#define DCICRC_ROI_START_OFFSET_Y_SHIFT 16
|
||||
#define DCICRC_ROI_CHANGED 0
|
||||
#define DCICRC_ROI_FROZEN (0x1 << 30)
|
||||
#define DCICRC_ROI_ENABLE (0x1 << 31)
|
||||
#define DCICRC_ROI_DISABLE 0
|
||||
|
||||
#define DCICRS_ROI_END_OFFSET_X_MASK 0x1FFF
|
||||
#define DCICRS_ROI_END_OFFSET_X_SHIFT 0
|
||||
#define DCICRS_ROI_END_OFFSET_Y_MASK (0xFFF << 16)
|
||||
#define DCICRS_ROI_END_OFFSET_Y_SHIFT 16
|
||||
|
||||
struct roi_regs {
|
||||
u32 dcicrc;
|
||||
u32 dcicrs;
|
||||
u32 dcicrrs;
|
||||
u32 dcicrcs;
|
||||
};
|
||||
|
||||
struct dcic_regs {
|
||||
u32 dcicc;
|
||||
u32 dcicic;
|
||||
u32 dcics;
|
||||
u32 dcic_reserved;
|
||||
struct roi_regs ROI[16];
|
||||
};
|
||||
|
||||
struct dcic_mux {
|
||||
char dcic[16];
|
||||
u32 val;
|
||||
};
|
||||
|
||||
struct bus_mux {
|
||||
char name[16];
|
||||
int reg;
|
||||
int shift;
|
||||
int mask;
|
||||
int dcic_mux_num;
|
||||
const struct dcic_mux *dcics;
|
||||
};
|
||||
|
||||
struct dcic_info {
|
||||
int bus_mux_num;
|
||||
const struct bus_mux *buses;
|
||||
};
|
||||
|
||||
struct dcic_data {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct dcic_regs *regs;
|
||||
const struct bus_mux *buses;
|
||||
u32 bus_n;
|
||||
u32 mux_n;
|
||||
struct clk *disp_axi_clk;
|
||||
struct clk *dcic_clk;
|
||||
struct mutex lock;
|
||||
struct completion roi_crc_comp;
|
||||
struct class *class;
|
||||
int major;
|
||||
struct cdev cdev; /* Char device structure */
|
||||
dev_t devt;
|
||||
unsigned int result;
|
||||
};
|
||||
#endif
|
Loading…
Reference in New Issue