diff options
Diffstat (limited to 'drivers/media/platform/exynos4-is/fimc-is.c')
| -rw-r--r-- | drivers/media/platform/exynos4-is/fimc-is.c | 193 |
1 files changed, 92 insertions, 101 deletions
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c index 47c6363d04e..5476dce3ad2 100644 --- a/drivers/media/platform/exynos4-is/fimc-is.c +++ b/drivers/media/platform/exynos4-is/fimc-is.c @@ -21,16 +21,16 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of_i2c.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/v4l2-of.h> #include <media/videobuf2-dma-contig.h> #include "media-dev.h" @@ -48,7 +48,6 @@ static char *fimc_is_clocks[ISS_CLKS_MAX] = { [ISS_CLK_LITE0] = "lite0", [ISS_CLK_LITE1] = "lite1", [ISS_CLK_MPLL] = "mpll", - [ISS_CLK_SYSREG] = "sysreg", [ISS_CLK_ISP] = "isp", [ISS_CLK_DRC] = "drc", [ISS_CLK_FD] = "fd", @@ -71,7 +70,6 @@ static void fimc_is_put_clocks(struct fimc_is *is) for (i = 0; i < ISS_CLKS_MAX; i++) { if (IS_ERR(is->clocks[i])) continue; - clk_unprepare(is->clocks[i]); clk_put(is->clocks[i]); is->clocks[i] = ERR_PTR(-EINVAL); } @@ -90,12 +88,6 @@ static int fimc_is_get_clocks(struct fimc_is *is) ret = PTR_ERR(is->clocks[i]); goto err; } - ret = clk_prepare(is->clocks[i]); - if (ret < 0) { - clk_put(is->clocks[i]); - is->clocks[i] = ERR_PTR(-EINVAL); - goto err; - } } return 0; @@ -103,7 +95,7 @@ err: fimc_is_put_clocks(is); dev_err(&is->pdev->dev, "failed to get clock: %s\n", fimc_is_clocks[i]); - return -ENXIO; + return ret; } static int fimc_is_setup_clocks(struct fimc_is *is) @@ -137,14 +129,14 @@ static int fimc_is_setup_clocks(struct fimc_is *is) ATCLK_MCUISP_FREQUENCY); } -int fimc_is_enable_clocks(struct fimc_is *is) +static int fimc_is_enable_clocks(struct fimc_is *is) { int i, ret; for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { if (IS_ERR(is->clocks[i])) continue; - ret = clk_enable(is->clocks[i]); + ret = clk_prepare_enable(is->clocks[i]); if (ret < 0) { dev_err(&is->pdev->dev, "clock %s enable failed\n", fimc_is_clocks[i]); @@ -157,90 +149,81 @@ int fimc_is_enable_clocks(struct fimc_is *is) return 0; } -void fimc_is_disable_clocks(struct fimc_is *is) +static void fimc_is_disable_clocks(struct fimc_is *is) { int i; for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { if (!IS_ERR(is->clocks[i])) { - clk_disable(is->clocks[i]); + clk_disable_unprepare(is->clocks[i]); pr_debug("disabled clock: %s\n", fimc_is_clocks[i]); } } } -static int fimc_is_parse_sensor_config(struct fimc_is_sensor *sensor, - struct device_node *np) +static int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, + struct device_node *node) { + struct fimc_is_sensor *sensor = &is->sensor[index]; u32 tmp = 0; int ret; - np = v4l2_of_get_next_endpoint(np, NULL); - if (!np) + sensor->drvdata = fimc_is_sensor_get_drvdata(node); + if (!sensor->drvdata) { + dev_err(&is->pdev->dev, "no driver data found for: %s\n", + node->full_name); + return -EINVAL; + } + + node = of_graph_get_next_endpoint(node, NULL); + if (!node) return -ENXIO; - np = v4l2_of_get_remote_port(np); - if (!np) + + node = of_graph_get_remote_port(node); + if (!node) return -ENXIO; /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ - ret = of_property_read_u32(np, "reg", &tmp); - sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; + 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; + } - return ret; + sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; + return 0; } static int fimc_is_register_subdevs(struct fimc_is *is) { - struct device_node *adapter, *child; - int ret; + struct device_node *i2c_bus, *child; + int ret, index = 0; ret = fimc_isp_subdev_create(&is->isp); if (ret < 0) return ret; - for_each_compatible_node(adapter, NULL, FIMC_IS_I2C_COMPATIBLE) { - if (!of_find_device_by_node(adapter)) { - of_node_put(adapter); - return -EPROBE_DEFER; - } - - for_each_available_child_of_node(adapter, child) { - struct i2c_client *client; - struct v4l2_subdev *sd; + /* Initialize memory allocator context for the ISP DMA. */ + is->isp.alloc_ctx = is->alloc_ctx; - client = of_find_i2c_device_by_node(child); - if (!client) - goto e_retry; + for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { + for_each_available_child_of_node(i2c_bus, child) { + ret = fimc_is_parse_sensor_config(is, index, child); - sd = i2c_get_clientdata(client); - if (!sd) - goto e_retry; - - /* FIXME: Add support for multiple sensors. */ - if (WARN_ON(is->sensor)) - continue; - - is->sensor = sd_to_fimc_is_sensor(sd); - - if (fimc_is_parse_sensor_config(is->sensor, child)) { - dev_warn(&is->pdev->dev, "DT parse error: %s\n", - child->full_name); + if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { + of_node_put(child); + return ret; } - pr_debug("%s(): registered subdev: %p\n", - __func__, sd->name); + index++; } } return 0; - -e_retry: - of_node_put(child); - return -EPROBE_DEFER; } static int fimc_is_unregister_subdevs(struct fimc_is *is) { fimc_isp_subdev_destroy(&is->isp); - is->sensor = NULL; return 0; } @@ -326,6 +309,11 @@ int fimc_is_start_firmware(struct fimc_is *is) struct device *dev = &is->pdev->dev; int ret; + if (is->fw.f_w == NULL) { + dev_err(dev, "firmware is not loaded\n"); + return -EINVAL; + } + memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size); wmb(); @@ -379,6 +367,9 @@ static void fimc_is_free_cpu_memory(struct fimc_is *is) { struct device *dev = &is->pdev->dev; + if (is->memory.vaddr == NULL) + return; + dma_free_coherent(dev, is->memory.size, is->memory.vaddr, is->memory.paddr); } @@ -530,8 +521,8 @@ static void fimc_is_general_irq_handler(struct fimc_is *is) break; case HIC_SET_PARAMETER: - is->config[is->config_index].p_region_index1 = 0; - is->config[is->config_index].p_region_index2 = 0; + is->config[is->config_index].p_region_index[0] = 0; + is->config[is->config_index].p_region_index[1] = 0; set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); pr_debug("HIC_SET_PARAMETER\n"); break; @@ -590,8 +581,8 @@ static void fimc_is_general_irq_handler(struct fimc_is *is) switch (is->i2h_cmd.args[0]) { case HIC_SET_PARAMETER: - is->config[is->config_index].p_region_index1 = 0; - is->config[is->config_index].p_region_index2 = 0; + is->config[is->config_index].p_region_index[0] = 0; + is->config[is->config_index].p_region_index[1] = 0; set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); break; } @@ -650,7 +641,7 @@ static int fimc_is_hw_open_sensor(struct fimc_is *is, fimc_is_hw_set_intgr0_gd0(is); return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, - FIMC_IS_SENSOR_OPEN_TIMEOUT); + sensor->drvdata->open_timeout); } @@ -664,8 +655,8 @@ int fimc_is_hw_initialize(struct fimc_is *is) u32 prev_id; int i, ret; - /* Sensor initialization. */ - ret = fimc_is_hw_open_sensor(is, is->sensor); + /* Sensor initialization. Only one sensor is currently supported. */ + ret = fimc_is_hw_open_sensor(is, &is->sensor[0]); if (ret < 0) return ret; @@ -784,6 +775,9 @@ static int fimc_is_debugfs_create(struct fimc_is *is) return is->debugfs_entry == NULL ? -EIO : 0; } +static int fimc_is_runtime_resume(struct device *dev); +static int fimc_is_runtime_suspend(struct device *dev); + static int fimc_is_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -837,27 +831,21 @@ static int fimc_is_probe(struct platform_device *pdev) goto err_clk; } pm_runtime_enable(dev); - /* - * Enable only the ISP power domain, keep FIMC-IS clocks off until - * the whole clock tree is configured. The ISP power domain needs - * be active in order to acces any CMU_ISP clock registers. - */ - ret = pm_runtime_get_sync(dev); - if (ret < 0) - goto err_irq; - ret = fimc_is_setup_clocks(is); - pm_runtime_put_sync(dev); + if (!pm_runtime_enabled(dev)) { + ret = fimc_is_runtime_resume(dev); + if (ret < 0) + goto err_irq; + } + ret = pm_runtime_get_sync(dev); if (ret < 0) - goto err_irq; - - is->clk_init = true; + goto err_pm; is->alloc_ctx = vb2_dma_contig_init_ctx(dev); if (IS_ERR(is->alloc_ctx)) { ret = PTR_ERR(is->alloc_ctx); - goto err_irq; + goto err_pm; } /* * Register FIMC-IS V4L2 subdevs to this driver. The video nodes @@ -875,15 +863,20 @@ static int fimc_is_probe(struct platform_device *pdev) if (ret < 0) goto err_dfs; + pm_runtime_put_sync(dev); + dev_dbg(dev, "FIMC-IS registered successfully\n"); return 0; err_dfs: fimc_is_debugfs_remove(is); -err_vb: - vb2_dma_contig_cleanup_ctx(is->alloc_ctx); err_sd: fimc_is_unregister_subdevs(is); +err_vb: + vb2_dma_contig_cleanup_ctx(is->alloc_ctx); +err_pm: + if (!pm_runtime_enabled(dev)) + fimc_is_runtime_suspend(dev); err_irq: free_irq(is->irq, is); err_clk: @@ -894,9 +887,11 @@ err_clk: static int fimc_is_runtime_resume(struct device *dev) { struct fimc_is *is = dev_get_drvdata(dev); + int ret; - if (!is->clk_init) - return 0; + ret = fimc_is_setup_clocks(is); + if (ret) + return ret; return fimc_is_enable_clocks(is); } @@ -905,9 +900,7 @@ static int fimc_is_runtime_suspend(struct device *dev) { struct fimc_is *is = dev_get_drvdata(dev); - if (is->clk_init) - fimc_is_disable_clocks(is); - + fimc_is_disable_clocks(is); return 0; } @@ -932,16 +925,20 @@ static int fimc_is_suspend(struct device *dev) static int fimc_is_remove(struct platform_device *pdev) { - struct fimc_is *is = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct fimc_is *is = dev_get_drvdata(dev); - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + if (!pm_runtime_status_suspended(dev)) + fimc_is_runtime_suspend(dev); free_irq(is->irq, is); fimc_is_unregister_subdevs(is); vb2_dma_contig_cleanup_ctx(is->alloc_ctx); fimc_is_put_clocks(is); fimc_is_debugfs_remove(is); - release_firmware(is->fw.f_w); + if (is->fw.f_w) + release_firmware(is->fw.f_w); fimc_is_free_cpu_memory(is); return 0; @@ -974,27 +971,20 @@ static int fimc_is_module_init(void) { int ret; - ret = fimc_is_register_sensor_driver(); - if (ret < 0) - return ret; - ret = fimc_is_register_i2c_driver(); if (ret < 0) - goto err_sens; + return ret; ret = platform_driver_register(&fimc_is_driver); - if (!ret) - return ret; - fimc_is_unregister_i2c_driver(); -err_sens: - fimc_is_unregister_sensor_driver(); + if (ret < 0) + fimc_is_unregister_i2c_driver(); + return ret; } static void fimc_is_module_exit(void) { - fimc_is_unregister_sensor_driver(); fimc_is_unregister_i2c_driver(); platform_driver_unregister(&fimc_is_driver); } @@ -1005,3 +995,4 @@ module_exit(fimc_is_module_exit); MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); +MODULE_LICENSE("GPL v2"); |
