summaryrefslogtreecommitdiff
path: root/drivers/media/platform/exynos5-is/fimc-is-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/exynos5-is/fimc-is-core.c')
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.c455
1 files changed, 334 insertions, 121 deletions
diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.c b/drivers/media/platform/exynos5-is/fimc-is-core.c
index 5fc077f616f..592479d9ec0 100644
--- a/drivers/media/platform/exynos5-is/fimc-is-core.c
+++ b/drivers/media/platform/exynos5-is/fimc-is-core.c
@@ -1,7 +1,7 @@
/*
* Samsung EXYNOS5 FIMC-IS (Imaging Subsystem) driver
*
- * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2013-2014 Samsung Electronics Co., Ltd.
* Arun Kumar K <arun.kk@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -26,11 +26,17 @@
#include "fimc-is-i2c.h"
#include "exynos5-mdev.h"
-#define CLK_MCU_ISP_DIV0_FREQ (200 * 1000000)
-#define CLK_MCU_ISP_DIV1_FREQ (100 * 1000000)
-#define CLK_ISP_DIV0_FREQ (134 * 1000000)
-#define CLK_ISP_DIV1_FREQ (68 * 1000000)
-#define CLK_ISP_DIVMPWM_FREQ (34 * 1000000)
+#define CLK_MCU_ISP_DIV0_FREQ (200 * 1000000)
+#define CLK_MCU_ISP_DIV1_FREQ (100 * 1000000)
+#define CLK_ISP_DIV0_FREQ (150 * 1000000)
+#define CLK_ISP_DIV1_FREQ (50 * 1000000)
+#define CLK_ISP_DIVMPWM_FREQ (50 /*75*/ * 1000000)
+#define CLK_MCU_ISP_ACLK_400_FREQ (400 * 1000000)
+#define CLK_DIV_ACLK_266_FREQ (/*300*/ 100 * 1000000)
+#define EXYNOS3250_CLK_ISP_DIV_FREQ (50 * 1000000)
+
+#define FIMC_IS_EXYNOS5_CLK_MASK 0x7f
+#define FIMC_IS_EXYNOS3250_CLK_MASK 0xff
static const char * const fimc_is_clock_name[] = {
[IS_CLK_ISP] = "isp",
@@ -40,74 +46,233 @@ static const char * const fimc_is_clock_name[] = {
[IS_CLK_ISP_DIVMPWM] = "isp_divmpwm",
[IS_CLK_MCU_ISP_DIV0] = "mcu_isp_div0",
[IS_CLK_MCU_ISP_DIV1] = "mcu_isp_div1",
+ /* Exynos 3250 */
+ [IS_CLK_CAM1] = "cam1",
+ /* ACLK 400 MCUISP */
+ [IS_CLK_MOUT_ACLK400_MCUISP_SUB] = "mout_aclk400_mcuisp_sub",
+ [IS_CLK_DIV_ACLK400_MCUISP] = "div_aclk400_mcuisp",
+ [IS_CLK_MOUT_ACLK400_MCUISP] = "mout_aclk400_mcuisp",
+ /* ACLK 266 */
+ [IS_CLK_MOUT_ACLK266_0] = "mout_aclk266_0",
+ [IS_CLK_MOUT_ACLK266] = "mout_aclk266",
+ [IS_CLK_DIV_ACLK266] = "div_aclk266",
+ [IS_CLK_MOUT_ACLK266_SUB] = "mout_aclk266_sub",
+ [IS_CLK_DIV_MPLL_PRE] = "div_mpll_pre",
+ [IS_CLK_FIN_PLL] = "fin_pll",
};
-static void fimc_is_put_clocks(struct fimc_is *is)
-{
- int i;
+#define FIMC_IS_CLK_ERR(dev, i , action) \
+ dev_err(dev, "Failed to "#action" for %s clock\n", \
+ fimc_is_clock_name[i]);
- for (i = 0; i < IS_CLK_MAX_NUM; i++) {
- if (IS_ERR(is->clock[i]))
- continue;
- clk_unprepare(is->clock[i]);
- clk_put(is->clock[i]);
- is->clock[i] = ERR_PTR(-EINVAL);
+static int fimc_is_exyno3250_clk_cfg(struct fimc_is *is)
+{
+ int ret;
+
+ /* DIV 400 MCUISP -> MUX ACLK 400 MCUISP SUB */
+ ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK400_MCUISP_SUB],
+ is->clocks[IS_CLK_DIV_ACLK400_MCUISP]);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK400_MCUISP_SUB,
+ set parentness);
+ return ret;
}
-}
-static int fimc_is_get_clocks(struct fimc_is *is)
-{
- struct device *dev = &is->pdev->dev;
- int i, ret;
-
- for (i = 0; i < IS_CLK_MAX_NUM; i++) {
- is->clock[i] = clk_get(dev, fimc_is_clock_name[i]);
- if (IS_ERR(is->clock[i]))
- goto err;
- ret = clk_prepare(is->clock[i]);
- if (ret < 0) {
- clk_put(is->clock[i]);
- is->clock[i] = ERR_PTR(-EINVAL);
- goto err;
- }
+ ret = clk_set_rate(is->clocks[IS_CLK_DIV_ACLK400_MCUISP],
+ CLK_MCU_ISP_ACLK_400_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_DIV_ACLK400_MCUISP,
+ set rate);
+ return ret;
+ }
+
+ ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV0],
+ CLK_MCU_ISP_DIV0_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MCU_ISP_DIV0,
+ set rate);
+ return ret;
}
- return 0;
-err:
- fimc_is_put_clocks(is);
- dev_err(dev, "Failed to get clock: %s\n", fimc_is_clock_name[i]);
- return -ENXIO;
-}
-static int fimc_is_configure_clocks(struct fimc_is *is)
-{
- int i, ret;
+ ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV1],
+ CLK_MCU_ISP_DIV1_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MCU_ISP_DIV1,
+ set rate);
+ return ret;
+ }
+
+ /* ACLK 266 */
+ clk_set_parent( is->clocks[IS_CLK_MOUT_ACLK266_0],
+ is->clocks[IS_CLK_DIV_MPLL_PRE]);
+
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266_0,
+ ser rate);
+ return ret;
+ }
+
+ ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266],
+ is->clocks[IS_CLK_MOUT_ACLK266_0]);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266,
+ set parentness);
+ return ret;
+ }
+
+ ret = clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266_SUB],
+ is->clocks[IS_CLK_DIV_ACLK266]);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_MOUT_ACLK266_SUB,
+ set parentness);
+ return ret;
+ }
+
+ ret = clk_set_rate(is->clocks[IS_CLK_DIV_ACLK266],
+ CLK_DIV_ACLK_266_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_DIV_ACLK266,
+ set rate);
+ return ret;
+ }
+
+ ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV0],
+ EXYNOS3250_CLK_ISP_DIV_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIV0,
+ set rate);
+ return ret;
+ }
+
+ ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV1],
+ EXYNOS3250_CLK_ISP_DIV_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIV1,
+ set rate);
+ return ret;
+ }
+
+ ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIVMPWM], CLK_ISP_DIVMPWM_FREQ);
+ if (ret) {
+ FIMC_IS_CLK_ERR(&is->pdev->dev, IS_CLK_ISP_DIVMPWM,
+ set rate);
+ return ret;
+ }
+
+ return ret;
- for (i = 0; i < IS_CLK_MAX_NUM; i++)
- is->clock[i] = ERR_PTR(-EINVAL);
+}
- ret = fimc_is_get_clocks(is);
- if (ret)
- return ret;
+static int fimc_is_exynos5_clk_cfg(struct fimc_is *is)
+{
+ int ret;
/* Set rates */
- ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV0],
+ ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV0],
CLK_MCU_ISP_DIV0_FREQ);
if (ret)
return ret;
- ret = clk_set_rate(is->clock[IS_CLK_MCU_ISP_DIV1],
+ ret = clk_set_rate(is->clocks[IS_CLK_MCU_ISP_DIV1],
CLK_MCU_ISP_DIV1_FREQ);
if (ret)
return ret;
- ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV0], CLK_ISP_DIV0_FREQ);
+ ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV0], CLK_ISP_DIV0_FREQ);
if (ret)
return ret;
- ret = clk_set_rate(is->clock[IS_CLK_ISP_DIV1], CLK_ISP_DIV1_FREQ);
+ ret = clk_set_rate(is->clocks[IS_CLK_ISP_DIV1], CLK_ISP_DIV1_FREQ);
if (ret)
return ret;
- return clk_set_rate(is->clock[IS_CLK_ISP_DIVMPWM],
+ return clk_set_rate(is->clocks[IS_CLK_ISP_DIVMPWM],
CLK_ISP_DIVMPWM_FREQ);
}
+static void fimc_is_put_clocks(struct fimc_is *is)
+{
+ int i;
+
+ for (i = IS_CLK_GATE_MAX; i < IS_CLKS_MAX; ++i) {
+ if (!IS_ERR(is->clocks[i]))
+ clk_unprepare(is->clocks[i]);
+
+ }
+ for (i = 0; i < IS_CLKS_MAX; ++i) {
+ if(!IS_ERR(is->clocks[i])) {
+ 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 < IS_CLKS_MAX; ++i)
+ is->clocks[i] = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < IS_CLK_GATE_MAX; ++i) {
+ if (!(is->drvdata->clk_mask & (0x1 << i)))
+ continue;
+ is->clocks[i] = clk_get(&is->pdev->dev,
+ fimc_is_clock_name[i]);
+ if (IS_ERR(is->clocks[i])) {
+ dev_err(&is->pdev->dev,
+ "Failed to acquire clock : %s\n",
+ fimc_is_clock_name[i]);
+ ret = PTR_ERR(is->clocks[i]);
+ goto rollback;
+ }
+ }
+
+ if (is->drvdata->variant == FIMC_IS_EXYNOS3250) {
+ for (i = IS_CLK_GATE_MAX; i < IS_CLKS_MAX ; ++i) {
+ is->clocks[i] = clk_get(&is->pdev->dev,
+ fimc_is_clock_name[i]);
+
+ if (IS_ERR(is->clocks[i])) {
+ dev_err(&is->pdev->dev,
+ "Failed to acquire clock: %s\n",
+ fimc_is_clock_name[i]);
+ ret = PTR_ERR(is->clocks[i]);
+ goto rollback;
+ }
+
+ ret = clk_prepare(is->clocks[i]);
+
+ if (ret) {
+ dev_err(&is->pdev->dev,
+ "Failed to prepare clock %s\n",
+ fimc_is_clock_name[i]);
+ goto rollback;
+ }
+ }
+ }
+
+ return 0;
+
+rollback:
+ fimc_is_put_clocks(is);
+ return ret;
+}
+
+static int fimc_is_configure_clocks(struct fimc_is* is)
+{
+ int ret = 0;
+
+ switch(is->drvdata->variant){
+ case FIMC_IS_EXYNOS5:
+ ret = fimc_is_exynos5_clk_cfg(is);
+ break;
+ case FIMC_IS_EXYNOS3250:
+ ret = fimc_is_exyno3250_clk_cfg(is);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
static void fimc_is_pipelines_destroy(struct fimc_is *is)
{
int i;
@@ -129,6 +294,13 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
node->full_name);
return -EINVAL;
}
+ ret = of_property_read_u32(node, "reg", &tmp);
+ if (ret < 0) {
+ dev_err(&is->pdev->dev, "reg property not found at: %s\n",
+ node->full_name);
+ return ret;
+ }
+ sensor->i2c_slave_addr = tmp;
node = v4l2_of_get_next_endpoint(node, NULL);
if (!node)
@@ -142,7 +314,7 @@ static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index,
ret = of_property_read_u32(node, "reg", &tmp);
if (ret < 0) {
dev_err(&is->pdev->dev, "reg property not found at: %s\n",
- node->full_name);
+ node->full_name);
return ret;
}
@@ -169,17 +341,25 @@ static int fimc_is_parse_sensor(struct fimc_is *is)
return 0;
}
-static struct fimc_is_drvdata exynos5250_drvdata = {
- .num_instances = 1,
- .fw_name = "exynos5_fimc_is_fw.bin",
-};
-
static struct fimc_is_drvdata exynos3250_drvdata = {
.num_instances = 1,
.fw_name = "exynos3_fimc_is_fw.bin",
+ .fw_version = FIMC_IS_FW_V130,
+ .clk_mask = FIMC_IS_EXYNOS3250_CLK_MASK,
+ .subip_mask = FIMC_IS_EXYNOS3250_SUBBLOCKS,
+ .variant = FIMC_IS_EXYNOS3250,
.master_node = 1,
};
+static struct fimc_is_drvdata exynos5250_drvdata = {
+ .num_instances = 1,
+ .fw_name = "exynos5_fimc_is_fw.bin",
+ .fw_version = FIMC_IS_FW_V120,
+ .clk_mask = FIMC_IS_EXYNOS5_CLK_MASK,
+ .subip_mask = FIMC_IS_EXYNOS5_SUBBLOCKS,
+ .variant = FIMC_IS_EXYNOS5,
+};
+
static const struct of_device_id fimc_is_of_match[] = {
{
.compatible = "samsung,exynos5250-fimc-is",
@@ -192,6 +372,92 @@ static const struct of_device_id fimc_is_of_match[] = {
};
MODULE_DEVICE_TABLE(of, fimc_is_of_match);
+int fimc_is_clk_enable(struct fimc_is *is)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < IS_CLK_GATE_MAX; ++i){
+ if (IS_ERR(is->clocks[i]))
+ continue;
+
+ ret = clk_prepare_enable(is->clocks[i]);
+ if (ret) {
+ dev_err(&is->pdev->dev,
+ "Failed to prepare and enable %s clock\n",
+ fimc_is_clock_name[i]);
+ for (; i >= 0; --i)
+ clk_disable_unprepare(is->clocks[i]);
+
+ }
+ }
+ return ret;
+}
+
+void fimc_is_clk_disable(struct fimc_is *is)
+{
+ int i;
+ for (i = 0; i < IS_CLK_GATE_MAX; ++i)
+ if (!IS_ERR(is->clocks[i]))
+ clk_disable_unprepare(is->clocks[i]);
+ if (is->drvdata->variant == FIMC_IS_EXYNOS3250) {
+ if (clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK400_MCUISP_SUB],
+ is->clocks[IS_CLK_FIN_PLL]))
+ FIMC_IS_CLK_ERR(&is->pdev->dev,
+ IS_CLK_MOUT_ACLK400_MCUISP_SUB,
+ change parentness);
+ if (clk_set_parent(is->clocks[IS_CLK_MOUT_ACLK266_SUB],
+ is->clocks[IS_CLK_FIN_PLL]))
+ FIMC_IS_CLK_ERR(&is->pdev->dev,
+ IS_CLK_MOUT_ACLK266_SUB,
+ change parentness);
+ }
+}
+
+static int fimc_is_pm_resume(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+ int ret;
+
+ fimc_is_configure_clocks(is);
+ ret = fimc_is_clk_enable(is);
+ if (ret < 0) {
+ dev_err(dev, "Could not enable clocks\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int fimc_is_pm_suspend(struct device *dev)
+{
+ struct fimc_is *is = dev_get_drvdata(dev);
+ fimc_is_clk_disable(is);
+ return 0;
+}
+
+static int fimc_is_runtime_resume(struct device *dev)
+{
+ return fimc_is_pm_resume(dev);
+}
+
+static int fimc_is_runtime_suspend(struct device *dev)
+{
+ return fimc_is_pm_suspend(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int fimc_is_resume(struct device *dev)
+{
+ /* TODO */
+ return 0;
+}
+
+static int fimc_is_suspend(struct device *dev)
+{
+ /* TODO */
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
static int fimc_is_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -239,7 +505,7 @@ static int fimc_is_probe(struct platform_device *pdev)
return irq;
}
- ret = fimc_is_configure_clocks(is);
+ ret = fimc_is_get_clocks(is);
if (ret < 0) {
dev_err(dev, "clocks configuration failed\n");
goto err_clk;
@@ -247,6 +513,14 @@ static int fimc_is_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, is);
pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = fimc_is_runtime_resume(dev);
+ if (ret) {
+ dev_err(dev, "Runtime resume failed\n");
+ goto err_clk;
+ }
+
+ }
is->alloc_ctx = vb2_dma_contig_init_ctx(dev);
if (IS_ERR(is->alloc_ctx)) {
@@ -282,6 +556,9 @@ static int fimc_is_probe(struct platform_device *pdev)
goto err_cam;
}
+ INIT_LIST_HEAD(&is->event_listeners);
+ spin_lock_init(&is->events_lock);
+
dev_dbg(dev, "FIMC-IS registered successfully\n");
return 0;
@@ -297,70 +574,6 @@ err_clk:
return ret;
}
-int fimc_is_clk_enable(struct fimc_is *is)
-{
- int ret;
-
- ret = clk_enable(is->clock[IS_CLK_ISP]);
- if (ret)
- return ret;
- ret = clk_enable(is->clock[IS_CLK_MCU_ISP]);
- if (ret)
- clk_disable(is->clock[IS_CLK_ISP]);
- return ret;
-}
-
-void fimc_is_clk_disable(struct fimc_is *is)
-{
- clk_disable(is->clock[IS_CLK_ISP]);
- clk_disable(is->clock[IS_CLK_MCU_ISP]);
-}
-
-static int fimc_is_pm_resume(struct device *dev)
-{
- struct fimc_is *is = dev_get_drvdata(dev);
- int ret;
-
- ret = fimc_is_clk_enable(is);
- if (ret < 0) {
- dev_err(dev, "Could not enable clocks\n");
- return ret;
- }
- return 0;
-}
-
-static int fimc_is_pm_suspend(struct device *dev)
-{
- struct fimc_is *is = dev_get_drvdata(dev);
-
- fimc_is_clk_disable(is);
- return 0;
-}
-
-static int fimc_is_runtime_resume(struct device *dev)
-{
- return fimc_is_pm_resume(dev);
-}
-
-static int fimc_is_runtime_suspend(struct device *dev)
-{
- return fimc_is_pm_suspend(dev);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int fimc_is_resume(struct device *dev)
-{
- /* TODO */
- return 0;
-}
-
-static int fimc_is_suspend(struct device *dev)
-{
- /* TODO */
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
static int fimc_is_remove(struct platform_device *pdev)
{
struct fimc_is *is = platform_get_drvdata(pdev);