/*
* Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
*
* Authors: Sylwester Nawrocki <s.nawrocki@samsung.com>
* Younghwan Joo <yhwan.joo@samsung.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.
*/
#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__
#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dma-contiguous.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include <media/videobuf2-dma-contig.h>
#include "media-dev.h"
#include "fimc-is.h"
#include "fimc-is-command.h"
#include "fimc-is-errno.h"
#include "fimc-is-i2c.h"
#include "fimc-is-param.h"
#include "fimc-is-regs.h"
static char *fimc_is_clocks[ISS_CLKS_MAX] = {
[ISS_CLK_PPMUISPX] = "ppmuispx",
[ISS_CLK_PPMUISPMX] = "ppmuispmx",
[ISS_CLK_LITE0] = "lite0",
[ISS_CLK_LITE1] = "lite1",
[ISS_CLK_MPLL] = "mpll",
[ISS_CLK_ISP] = "isp",
[ISS_CLK_DRC] = "drc",
[ISS_CLK_FD] = "fd",
[ISS_CLK_MCUISP] = "mcuisp",
[ISS_CLK_GICISP] = "gicisp",
[ISS_CLK_PWM_ISP] = "pwm_isp",
[ISS_CLK_MCUCTL_ISP] = "mcuctl_isp",
[ISS_CLK_UART] = "uart",
[ISS_CLK_ISP_DIV0] = "ispdiv0",
[ISS_CLK_ISP_DIV1] = "ispdiv1",
[ISS_CLK_MCUISP_DIV0] = "mcuispdiv0",
[ISS_CLK_MCUISP_DIV1] = "mcuispdiv1",
[ISS_CLK_ACLK200] = "aclk200",
[ISS_CLK_ACLK200_DIV] = "div_aclk200",
[ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp",
[ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp",
};
static void fimc_is_put_clocks(struct fimc_is *is)
{
int i;
for (i = 0; i < ISS_CLKS_MAX; i++) {
if (IS_ERR(is->clocks[i]))
continue;
clk_put(is->clocks[i]);
is->clocks[i] = ERR_PTR(-EINVAL);
}
}
static int fimc_is_get_clocks(struct fimc_is *is)
{
int i, ret;
for (i = 0; i < ISS_CLKS_MAX; i++)
is->clocks[i] = ERR_PTR(-EINVAL);
for (i = 0; i < ISS_CLKS_MAX; i++) {
is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]);
if (IS_ERR(is->clocks[i])) {
ret = PTR_ERR(is->clocks[i]);
goto err;
}
}
return 0;
err:
fimc_is_put_clocks(is);
dev_err(&is->pdev->dev, "failed to get clock: %s\n",
fimc_is_clocks[i]);
return ret;
}
static int fimc_is_setup_clocks(struct fimc_is *is)
{
int ret;
ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200],
is->clocks[ISS_CLK_ACLK200_DIV]);
if (ret < 0)
return ret;
ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP],
is->clocks[ISS_CLK_ACLK400MCUISP_DIV]);
if (ret < 0)
return ret;
ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY);
if (ret < 0)
return ret;
ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY);
if (ret < 0)
return ret;
ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0],
ATCLK_MCUISP_FREQUENCY);
if (ret < 0)
return ret;
return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1],
ATCLK_MCUISP_FREQUENCY);
}
static int fimc_is_enable_clocks(struct fimc_is *is)
{
int i, ret;
for (i = 0; i &l