162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Samsung Electronics Co., Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Sylwester Nawrocki <s.nawrocki@samsung.com> 862306a36Sopenharmony_ci * Younghwan Joo <yhwan.joo@samsung.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/debugfs.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/firmware.h> 1762306a36Sopenharmony_ci#include <linux/interrupt.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/i2c.h> 2162306a36Sopenharmony_ci#include <linux/of_irq.h> 2262306a36Sopenharmony_ci#include <linux/of_address.h> 2362306a36Sopenharmony_ci#include <linux/of_graph.h> 2462306a36Sopenharmony_ci#include <linux/of_platform.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/types.h> 2962306a36Sopenharmony_ci#include <linux/videodev2.h> 3062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "media-dev.h" 3362306a36Sopenharmony_ci#include "fimc-is.h" 3462306a36Sopenharmony_ci#include "fimc-is-command.h" 3562306a36Sopenharmony_ci#include "fimc-is-errno.h" 3662306a36Sopenharmony_ci#include "fimc-is-i2c.h" 3762306a36Sopenharmony_ci#include "fimc-is-param.h" 3862306a36Sopenharmony_ci#include "fimc-is-regs.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic char *fimc_is_clocks[ISS_CLKS_MAX] = { 4262306a36Sopenharmony_ci [ISS_CLK_PPMUISPX] = "ppmuispx", 4362306a36Sopenharmony_ci [ISS_CLK_PPMUISPMX] = "ppmuispmx", 4462306a36Sopenharmony_ci [ISS_CLK_LITE0] = "lite0", 4562306a36Sopenharmony_ci [ISS_CLK_LITE1] = "lite1", 4662306a36Sopenharmony_ci [ISS_CLK_MPLL] = "mpll", 4762306a36Sopenharmony_ci [ISS_CLK_ISP] = "isp", 4862306a36Sopenharmony_ci [ISS_CLK_DRC] = "drc", 4962306a36Sopenharmony_ci [ISS_CLK_FD] = "fd", 5062306a36Sopenharmony_ci [ISS_CLK_MCUISP] = "mcuisp", 5162306a36Sopenharmony_ci [ISS_CLK_GICISP] = "gicisp", 5262306a36Sopenharmony_ci [ISS_CLK_PWM_ISP] = "pwm_isp", 5362306a36Sopenharmony_ci [ISS_CLK_MCUCTL_ISP] = "mcuctl_isp", 5462306a36Sopenharmony_ci [ISS_CLK_UART] = "uart", 5562306a36Sopenharmony_ci [ISS_CLK_ISP_DIV0] = "ispdiv0", 5662306a36Sopenharmony_ci [ISS_CLK_ISP_DIV1] = "ispdiv1", 5762306a36Sopenharmony_ci [ISS_CLK_MCUISP_DIV0] = "mcuispdiv0", 5862306a36Sopenharmony_ci [ISS_CLK_MCUISP_DIV1] = "mcuispdiv1", 5962306a36Sopenharmony_ci [ISS_CLK_ACLK200] = "aclk200", 6062306a36Sopenharmony_ci [ISS_CLK_ACLK200_DIV] = "div_aclk200", 6162306a36Sopenharmony_ci [ISS_CLK_ACLK400MCUISP] = "aclk400mcuisp", 6262306a36Sopenharmony_ci [ISS_CLK_ACLK400MCUISP_DIV] = "div_aclk400mcuisp", 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void fimc_is_put_clocks(struct fimc_is *is) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci int i; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci for (i = 0; i < ISS_CLKS_MAX; i++) { 7062306a36Sopenharmony_ci if (IS_ERR(is->clocks[i])) 7162306a36Sopenharmony_ci continue; 7262306a36Sopenharmony_ci clk_put(is->clocks[i]); 7362306a36Sopenharmony_ci is->clocks[i] = ERR_PTR(-EINVAL); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int fimc_is_get_clocks(struct fimc_is *is) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int i, ret; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < ISS_CLKS_MAX; i++) 8262306a36Sopenharmony_ci is->clocks[i] = ERR_PTR(-EINVAL); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci for (i = 0; i < ISS_CLKS_MAX; i++) { 8562306a36Sopenharmony_ci is->clocks[i] = clk_get(&is->pdev->dev, fimc_is_clocks[i]); 8662306a36Sopenharmony_ci if (IS_ERR(is->clocks[i])) { 8762306a36Sopenharmony_ci ret = PTR_ERR(is->clocks[i]); 8862306a36Sopenharmony_ci goto err; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_cierr: 9462306a36Sopenharmony_ci fimc_is_put_clocks(is); 9562306a36Sopenharmony_ci dev_err(&is->pdev->dev, "failed to get clock: %s\n", 9662306a36Sopenharmony_ci fimc_is_clocks[i]); 9762306a36Sopenharmony_ci return ret; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int fimc_is_setup_clocks(struct fimc_is *is) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci int ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci ret = clk_set_parent(is->clocks[ISS_CLK_ACLK200], 10562306a36Sopenharmony_ci is->clocks[ISS_CLK_ACLK200_DIV]); 10662306a36Sopenharmony_ci if (ret < 0) 10762306a36Sopenharmony_ci return ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = clk_set_parent(is->clocks[ISS_CLK_ACLK400MCUISP], 11062306a36Sopenharmony_ci is->clocks[ISS_CLK_ACLK400MCUISP_DIV]); 11162306a36Sopenharmony_ci if (ret < 0) 11262306a36Sopenharmony_ci return ret; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV0], ACLK_AXI_FREQUENCY); 11562306a36Sopenharmony_ci if (ret < 0) 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = clk_set_rate(is->clocks[ISS_CLK_ISP_DIV1], ACLK_AXI_FREQUENCY); 11962306a36Sopenharmony_ci if (ret < 0) 12062306a36Sopenharmony_ci return ret; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci ret = clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV0], 12362306a36Sopenharmony_ci ATCLK_MCUISP_FREQUENCY); 12462306a36Sopenharmony_ci if (ret < 0) 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return clk_set_rate(is->clocks[ISS_CLK_MCUISP_DIV1], 12862306a36Sopenharmony_ci ATCLK_MCUISP_FREQUENCY); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int fimc_is_enable_clocks(struct fimc_is *is) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci int i, ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { 13662306a36Sopenharmony_ci if (IS_ERR(is->clocks[i])) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci ret = clk_prepare_enable(is->clocks[i]); 13962306a36Sopenharmony_ci if (ret < 0) { 14062306a36Sopenharmony_ci dev_err(&is->pdev->dev, "clock %s enable failed\n", 14162306a36Sopenharmony_ci fimc_is_clocks[i]); 14262306a36Sopenharmony_ci for (--i; i >= 0; i--) 14362306a36Sopenharmony_ci clk_disable_unprepare(is->clocks[i]); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci pr_debug("enabled clock: %s\n", fimc_is_clocks[i]); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void fimc_is_disable_clocks(struct fimc_is *is) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int i; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (i = 0; i < ISS_GATE_CLKS_MAX; i++) { 15662306a36Sopenharmony_ci if (!IS_ERR(is->clocks[i])) { 15762306a36Sopenharmony_ci clk_disable_unprepare(is->clocks[i]); 15862306a36Sopenharmony_ci pr_debug("disabled clock: %s\n", fimc_is_clocks[i]); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int fimc_is_parse_sensor_config(struct fimc_is *is, unsigned int index, 16462306a36Sopenharmony_ci struct device_node *node) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct fimc_is_sensor *sensor = &is->sensor[index]; 16762306a36Sopenharmony_ci struct device_node *ep, *port; 16862306a36Sopenharmony_ci u32 tmp = 0; 16962306a36Sopenharmony_ci int ret; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci sensor->drvdata = fimc_is_sensor_get_drvdata(node); 17262306a36Sopenharmony_ci if (!sensor->drvdata) { 17362306a36Sopenharmony_ci dev_err(&is->pdev->dev, "no driver data found for: %pOF\n", 17462306a36Sopenharmony_ci node); 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ep = of_graph_get_next_endpoint(node, NULL); 17962306a36Sopenharmony_ci if (!ep) 18062306a36Sopenharmony_ci return -ENXIO; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci port = of_graph_get_remote_port(ep); 18362306a36Sopenharmony_ci of_node_put(ep); 18462306a36Sopenharmony_ci if (!port) 18562306a36Sopenharmony_ci return -ENXIO; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Use MIPI-CSIS channel id to determine the ISP I2C bus index. */ 18862306a36Sopenharmony_ci ret = of_property_read_u32(port, "reg", &tmp); 18962306a36Sopenharmony_ci if (ret < 0) { 19062306a36Sopenharmony_ci dev_err(&is->pdev->dev, "reg property not found at: %pOF\n", 19162306a36Sopenharmony_ci port); 19262306a36Sopenharmony_ci of_node_put(port); 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci of_node_put(port); 19762306a36Sopenharmony_ci sensor->i2c_bus = tmp - FIMC_INPUT_MIPI_CSI2_0; 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int fimc_is_register_subdevs(struct fimc_is *is) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct device_node *i2c_bus, *child; 20462306a36Sopenharmony_ci int ret, index = 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ret = fimc_isp_subdev_create(&is->isp); 20762306a36Sopenharmony_ci if (ret < 0) 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) { 21162306a36Sopenharmony_ci for_each_available_child_of_node(i2c_bus, child) { 21262306a36Sopenharmony_ci ret = fimc_is_parse_sensor_config(is, index, child); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (ret < 0 || index >= FIMC_IS_SENSORS_NUM) { 21562306a36Sopenharmony_ci of_node_put(child); 21662306a36Sopenharmony_ci of_node_put(i2c_bus); 21762306a36Sopenharmony_ci return ret; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci index++; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int fimc_is_unregister_subdevs(struct fimc_is *is) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci fimc_isp_subdev_destroy(&is->isp); 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int fimc_is_load_setfile(struct fimc_is *is, char *file_name) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci const struct firmware *fw; 23462306a36Sopenharmony_ci void *buf; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci ret = request_firmware(&fw, file_name, &is->pdev->dev); 23862306a36Sopenharmony_ci if (ret < 0) { 23962306a36Sopenharmony_ci dev_err(&is->pdev->dev, "firmware request failed (%d)\n", ret); 24062306a36Sopenharmony_ci return ret; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci buf = is->memory.vaddr + is->setfile.base; 24362306a36Sopenharmony_ci memcpy(buf, fw->data, fw->size); 24462306a36Sopenharmony_ci fimc_is_mem_barrier(); 24562306a36Sopenharmony_ci is->setfile.size = fw->size; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pr_debug("mem vaddr: %p, setfile buf: %p\n", is->memory.vaddr, buf); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci memcpy(is->fw.setfile_info, 25062306a36Sopenharmony_ci fw->data + fw->size - FIMC_IS_SETFILE_INFO_LEN, 25162306a36Sopenharmony_ci FIMC_IS_SETFILE_INFO_LEN - 1); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci is->fw.setfile_info[FIMC_IS_SETFILE_INFO_LEN - 1] = '\0'; 25462306a36Sopenharmony_ci is->setfile.state = 1; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci pr_debug("FIMC-IS setfile loaded: base: %#x, size: %zu B\n", 25762306a36Sopenharmony_ci is->setfile.base, fw->size); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci release_firmware(fw); 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ciint fimc_is_cpu_set_power(struct fimc_is *is, int on) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci unsigned int timeout = FIMC_IS_POWER_ON_TIMEOUT; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (on) { 26862306a36Sopenharmony_ci /* Disable watchdog */ 26962306a36Sopenharmony_ci mcuctl_write(0, is, REG_WDT_ISP); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Cortex-A5 start address setting */ 27262306a36Sopenharmony_ci mcuctl_write(is->memory.addr, is, MCUCTL_REG_BBOAR); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Enable and start Cortex-A5 */ 27562306a36Sopenharmony_ci pmuisp_write(0x18000, is, REG_PMU_ISP_ARM_OPTION); 27662306a36Sopenharmony_ci pmuisp_write(0x1, is, REG_PMU_ISP_ARM_CONFIGURATION); 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci /* A5 power off */ 27962306a36Sopenharmony_ci pmuisp_write(0x10000, is, REG_PMU_ISP_ARM_OPTION); 28062306a36Sopenharmony_ci pmuisp_write(0x0, is, REG_PMU_ISP_ARM_CONFIGURATION); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci while (pmuisp_read(is, REG_PMU_ISP_ARM_STATUS) & 1) { 28362306a36Sopenharmony_ci if (timeout == 0) 28462306a36Sopenharmony_ci return -ETIME; 28562306a36Sopenharmony_ci timeout--; 28662306a36Sopenharmony_ci udelay(1); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci/* Wait until @bit of @is->state is set to @state in the interrupt handler. */ 29462306a36Sopenharmony_ciint fimc_is_wait_event(struct fimc_is *is, unsigned long bit, 29562306a36Sopenharmony_ci unsigned int state, unsigned int timeout) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci int ret = wait_event_timeout(is->irq_queue, 29962306a36Sopenharmony_ci !state ^ test_bit(bit, &is->state), 30062306a36Sopenharmony_ci timeout); 30162306a36Sopenharmony_ci if (ret == 0) { 30262306a36Sopenharmony_ci dev_WARN(&is->pdev->dev, "%s() timed out\n", __func__); 30362306a36Sopenharmony_ci return -ETIME; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ciint fimc_is_start_firmware(struct fimc_is *is) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct device *dev = &is->pdev->dev; 31162306a36Sopenharmony_ci int ret; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (is->fw.f_w == NULL) { 31462306a36Sopenharmony_ci dev_err(dev, "firmware is not loaded\n"); 31562306a36Sopenharmony_ci return -EINVAL; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci memcpy(is->memory.vaddr, is->fw.f_w->data, is->fw.f_w->size); 31962306a36Sopenharmony_ci wmb(); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = fimc_is_cpu_set_power(is, 1); 32262306a36Sopenharmony_ci if (ret < 0) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci ret = fimc_is_wait_event(is, IS_ST_A5_PWR_ON, 1, 32662306a36Sopenharmony_ci msecs_to_jiffies(FIMC_IS_FW_LOAD_TIMEOUT)); 32762306a36Sopenharmony_ci if (ret < 0) 32862306a36Sopenharmony_ci dev_err(dev, "FIMC-IS CPU power on failed\n"); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return ret; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* Allocate working memory for the FIMC-IS CPU. */ 33462306a36Sopenharmony_cistatic int fimc_is_alloc_cpu_memory(struct fimc_is *is) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct device *dev = &is->pdev->dev; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci is->memory.vaddr = dma_alloc_coherent(dev, FIMC_IS_CPU_MEM_SIZE, 33962306a36Sopenharmony_ci &is->memory.addr, GFP_KERNEL); 34062306a36Sopenharmony_ci if (is->memory.vaddr == NULL) 34162306a36Sopenharmony_ci return -ENOMEM; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci is->memory.size = FIMC_IS_CPU_MEM_SIZE; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dev_info(dev, "FIMC-IS CPU memory base: %pad\n", &is->memory.addr); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (((u32)is->memory.addr) & FIMC_IS_FW_ADDR_MASK) { 34862306a36Sopenharmony_ci dev_err(dev, "invalid firmware memory alignment: %#x\n", 34962306a36Sopenharmony_ci (u32)is->memory.addr); 35062306a36Sopenharmony_ci dma_free_coherent(dev, is->memory.size, is->memory.vaddr, 35162306a36Sopenharmony_ci is->memory.addr); 35262306a36Sopenharmony_ci return -EIO; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci is->is_p_region = (struct is_region *)(is->memory.vaddr + 35662306a36Sopenharmony_ci FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci is->is_dma_p_region = is->memory.addr + 35962306a36Sopenharmony_ci FIMC_IS_CPU_MEM_SIZE - FIMC_IS_REGION_SIZE; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci is->is_shared_region = (struct is_share_region *)(is->memory.vaddr + 36262306a36Sopenharmony_ci FIMC_IS_SHARED_REGION_OFFSET); 36362306a36Sopenharmony_ci return 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic void fimc_is_free_cpu_memory(struct fimc_is *is) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct device *dev = &is->pdev->dev; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (is->memory.vaddr == NULL) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci dma_free_coherent(dev, is->memory.size, is->memory.vaddr, 37462306a36Sopenharmony_ci is->memory.addr); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void fimc_is_load_firmware(const struct firmware *fw, void *context) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct fimc_is *is = context; 38062306a36Sopenharmony_ci struct device *dev = &is->pdev->dev; 38162306a36Sopenharmony_ci void *buf; 38262306a36Sopenharmony_ci int ret; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (fw == NULL) { 38562306a36Sopenharmony_ci dev_err(dev, "firmware request failed\n"); 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci mutex_lock(&is->lock); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (fw->size < FIMC_IS_FW_SIZE_MIN || fw->size > FIMC_IS_FW_SIZE_MAX) { 39162306a36Sopenharmony_ci dev_err(dev, "wrong firmware size: %zu\n", fw->size); 39262306a36Sopenharmony_ci goto done; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci is->fw.size = fw->size; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = fimc_is_alloc_cpu_memory(is); 39862306a36Sopenharmony_ci if (ret < 0) { 39962306a36Sopenharmony_ci dev_err(dev, "failed to allocate FIMC-IS CPU memory\n"); 40062306a36Sopenharmony_ci goto done; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci memcpy(is->memory.vaddr, fw->data, fw->size); 40462306a36Sopenharmony_ci wmb(); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Read firmware description. */ 40762306a36Sopenharmony_ci buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_DESC_LEN); 40862306a36Sopenharmony_ci memcpy(&is->fw.info, buf, FIMC_IS_FW_INFO_LEN); 40962306a36Sopenharmony_ci is->fw.info[FIMC_IS_FW_INFO_LEN] = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci buf = (void *)(is->memory.vaddr + fw->size - FIMC_IS_FW_VER_LEN); 41262306a36Sopenharmony_ci memcpy(&is->fw.version, buf, FIMC_IS_FW_VER_LEN); 41362306a36Sopenharmony_ci is->fw.version[FIMC_IS_FW_VER_LEN - 1] = 0; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci is->fw.state = 1; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci dev_info(dev, "loaded firmware: %s, rev. %s\n", 41862306a36Sopenharmony_ci is->fw.info, is->fw.version); 41962306a36Sopenharmony_ci dev_dbg(dev, "FW size: %zu, DMA addr: %pad\n", fw->size, &is->memory.addr); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci is->is_shared_region->chip_id = 0xe4412; 42262306a36Sopenharmony_ci is->is_shared_region->chip_rev_no = 1; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci fimc_is_mem_barrier(); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* 42762306a36Sopenharmony_ci * FIXME: The firmware is not being released for now, as it is 42862306a36Sopenharmony_ci * needed around for copying to the IS working memory every 42962306a36Sopenharmony_ci * time before the Cortex-A5 is restarted. 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ci release_firmware(is->fw.f_w); 43262306a36Sopenharmony_ci is->fw.f_w = fw; 43362306a36Sopenharmony_cidone: 43462306a36Sopenharmony_ci mutex_unlock(&is->lock); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int fimc_is_request_firmware(struct fimc_is *is, const char *fw_name) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci return request_firmware_nowait(THIS_MODULE, 44062306a36Sopenharmony_ci FW_ACTION_UEVENT, fw_name, &is->pdev->dev, 44162306a36Sopenharmony_ci GFP_KERNEL, is, fimc_is_load_firmware); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* General IS interrupt handler */ 44562306a36Sopenharmony_cistatic void fimc_is_general_irq_handler(struct fimc_is *is) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci is->i2h_cmd.cmd = mcuctl_read(is, MCUCTL_REG_ISSR(10)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci switch (is->i2h_cmd.cmd) { 45062306a36Sopenharmony_ci case IHC_GET_SENSOR_NUM: 45162306a36Sopenharmony_ci fimc_is_hw_get_params(is, 1); 45262306a36Sopenharmony_ci fimc_is_hw_wait_intmsr0_intmsd0(is); 45362306a36Sopenharmony_ci fimc_is_hw_set_sensor_num(is); 45462306a36Sopenharmony_ci pr_debug("ISP FW version: %#x\n", is->i2h_cmd.args[0]); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case IHC_SET_FACE_MARK: 45762306a36Sopenharmony_ci case IHC_FRAME_DONE: 45862306a36Sopenharmony_ci fimc_is_hw_get_params(is, 2); 45962306a36Sopenharmony_ci break; 46062306a36Sopenharmony_ci case IHC_SET_SHOT_MARK: 46162306a36Sopenharmony_ci case IHC_AA_DONE: 46262306a36Sopenharmony_ci case IH_REPLY_DONE: 46362306a36Sopenharmony_ci fimc_is_hw_get_params(is, 3); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case IH_REPLY_NOT_DONE: 46662306a36Sopenharmony_ci fimc_is_hw_get_params(is, 4); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci case IHC_NOT_READY: 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci default: 47162306a36Sopenharmony_ci pr_info("unknown command: %#x\n", is->i2h_cmd.cmd); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci fimc_is_fw_clear_irq1(is, FIMC_IS_INT_GENERAL); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci switch (is->i2h_cmd.cmd) { 47762306a36Sopenharmony_ci case IHC_GET_SENSOR_NUM: 47862306a36Sopenharmony_ci fimc_is_hw_set_intgr0_gd0(is); 47962306a36Sopenharmony_ci set_bit(IS_ST_A5_PWR_ON, &is->state); 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci case IHC_SET_SHOT_MARK: 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci case IHC_SET_FACE_MARK: 48662306a36Sopenharmony_ci is->fd_header.count = is->i2h_cmd.args[0]; 48762306a36Sopenharmony_ci is->fd_header.index = is->i2h_cmd.args[1]; 48862306a36Sopenharmony_ci is->fd_header.offset = 0; 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci case IHC_FRAME_DONE: 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci case IHC_AA_DONE: 49562306a36Sopenharmony_ci pr_debug("AA_DONE - %d, %d, %d\n", is->i2h_cmd.args[0], 49662306a36Sopenharmony_ci is->i2h_cmd.args[1], is->i2h_cmd.args[2]); 49762306a36Sopenharmony_ci break; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci case IH_REPLY_DONE: 50062306a36Sopenharmony_ci pr_debug("ISR_DONE: args[0]: %#x\n", is->i2h_cmd.args[0]); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci switch (is->i2h_cmd.args[0]) { 50362306a36Sopenharmony_ci case HIC_PREVIEW_STILL...HIC_CAPTURE_VIDEO: 50462306a36Sopenharmony_ci /* Get CAC margin */ 50562306a36Sopenharmony_ci set_bit(IS_ST_CHANGE_MODE, &is->state); 50662306a36Sopenharmony_ci is->isp.cac_margin_x = is->i2h_cmd.args[1]; 50762306a36Sopenharmony_ci is->isp.cac_margin_y = is->i2h_cmd.args[2]; 50862306a36Sopenharmony_ci pr_debug("CAC margin (x,y): (%d,%d)\n", 50962306a36Sopenharmony_ci is->isp.cac_margin_x, is->isp.cac_margin_y); 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci case HIC_STREAM_ON: 51362306a36Sopenharmony_ci clear_bit(IS_ST_STREAM_OFF, &is->state); 51462306a36Sopenharmony_ci set_bit(IS_ST_STREAM_ON, &is->state); 51562306a36Sopenharmony_ci break; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci case HIC_STREAM_OFF: 51862306a36Sopenharmony_ci clear_bit(IS_ST_STREAM_ON, &is->state); 51962306a36Sopenharmony_ci set_bit(IS_ST_STREAM_OFF, &is->state); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci case HIC_SET_PARAMETER: 52362306a36Sopenharmony_ci is->config[is->config_index].p_region_index[0] = 0; 52462306a36Sopenharmony_ci is->config[is->config_index].p_region_index[1] = 0; 52562306a36Sopenharmony_ci set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); 52662306a36Sopenharmony_ci pr_debug("HIC_SET_PARAMETER\n"); 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci case HIC_GET_PARAMETER: 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci case HIC_SET_TUNE: 53362306a36Sopenharmony_ci break; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci case HIC_GET_STATUS: 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci case HIC_OPEN_SENSOR: 53962306a36Sopenharmony_ci set_bit(IS_ST_OPEN_SENSOR, &is->state); 54062306a36Sopenharmony_ci pr_debug("data lanes: %d, settle line: %d\n", 54162306a36Sopenharmony_ci is->i2h_cmd.args[2], is->i2h_cmd.args[1]); 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci case HIC_CLOSE_SENSOR: 54562306a36Sopenharmony_ci clear_bit(IS_ST_OPEN_SENSOR, &is->state); 54662306a36Sopenharmony_ci is->sensor_index = 0; 54762306a36Sopenharmony_ci break; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci case HIC_MSG_TEST: 55062306a36Sopenharmony_ci pr_debug("config MSG level completed\n"); 55162306a36Sopenharmony_ci break; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci case HIC_POWER_DOWN: 55462306a36Sopenharmony_ci clear_bit(IS_ST_PWR_SUBIP_ON, &is->state); 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci case HIC_GET_SET_FILE_ADDR: 55862306a36Sopenharmony_ci is->setfile.base = is->i2h_cmd.args[1]; 55962306a36Sopenharmony_ci set_bit(IS_ST_SETFILE_LOADED, &is->state); 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci case HIC_LOAD_SET_FILE: 56362306a36Sopenharmony_ci set_bit(IS_ST_SETFILE_LOADED, &is->state); 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci case IH_REPLY_NOT_DONE: 56962306a36Sopenharmony_ci pr_err("ISR_NDONE: %d: %#x, %s\n", is->i2h_cmd.args[0], 57062306a36Sopenharmony_ci is->i2h_cmd.args[1], 57162306a36Sopenharmony_ci fimc_is_strerr(is->i2h_cmd.args[1])); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (is->i2h_cmd.args[1] & IS_ERROR_TIME_OUT_FLAG) 57462306a36Sopenharmony_ci pr_err("IS_ERROR_TIME_OUT\n"); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci switch (is->i2h_cmd.args[1]) { 57762306a36Sopenharmony_ci case IS_ERROR_SET_PARAMETER: 57862306a36Sopenharmony_ci fimc_is_mem_barrier(); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci switch (is->i2h_cmd.args[0]) { 58262306a36Sopenharmony_ci case HIC_SET_PARAMETER: 58362306a36Sopenharmony_ci is->config[is->config_index].p_region_index[0] = 0; 58462306a36Sopenharmony_ci is->config[is->config_index].p_region_index[1] = 0; 58562306a36Sopenharmony_ci set_bit(IS_ST_BLOCK_CMD_CLEARED, &is->state); 58662306a36Sopenharmony_ci break; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci case IHC_NOT_READY: 59162306a36Sopenharmony_ci pr_err("IS control sequence error: Not Ready\n"); 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci wake_up(&is->irq_queue); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic irqreturn_t fimc_is_irq_handler(int irq, void *priv) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct fimc_is *is = priv; 60162306a36Sopenharmony_ci unsigned long flags; 60262306a36Sopenharmony_ci u32 status; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci spin_lock_irqsave(&is->slock, flags); 60562306a36Sopenharmony_ci status = mcuctl_read(is, MCUCTL_REG_INTSR1); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (status & (1UL << FIMC_IS_INT_GENERAL)) 60862306a36Sopenharmony_ci fimc_is_general_irq_handler(is); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (status & (1UL << FIMC_IS_INT_FRAME_DONE_ISP)) 61162306a36Sopenharmony_ci fimc_isp_irq_handler(is); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci spin_unlock_irqrestore(&is->slock, flags); 61462306a36Sopenharmony_ci return IRQ_HANDLED; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int fimc_is_hw_open_sensor(struct fimc_is *is, 61862306a36Sopenharmony_ci struct fimc_is_sensor *sensor) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct sensor_open_extended *soe = (void *)&is->is_p_region->shared; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci fimc_is_hw_wait_intmsr0_intmsd0(is); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci soe->self_calibration_mode = 1; 62562306a36Sopenharmony_ci soe->actuator_type = 0; 62662306a36Sopenharmony_ci soe->mipi_lane_num = 0; 62762306a36Sopenharmony_ci soe->mclk = 0; 62862306a36Sopenharmony_ci soe->mipi_speed = 0; 62962306a36Sopenharmony_ci soe->fast_open_sensor = 0; 63062306a36Sopenharmony_ci soe->i2c_sclk = 88000000; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci fimc_is_mem_barrier(); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci /* 63562306a36Sopenharmony_ci * Some user space use cases hang up here without this 63662306a36Sopenharmony_ci * empirically chosen delay. 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_ci udelay(100); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci mcuctl_write(HIC_OPEN_SENSOR, is, MCUCTL_REG_ISSR(0)); 64162306a36Sopenharmony_ci mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1)); 64262306a36Sopenharmony_ci mcuctl_write(sensor->drvdata->id, is, MCUCTL_REG_ISSR(2)); 64362306a36Sopenharmony_ci mcuctl_write(sensor->i2c_bus, is, MCUCTL_REG_ISSR(3)); 64462306a36Sopenharmony_ci mcuctl_write(is->is_dma_p_region, is, MCUCTL_REG_ISSR(4)); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci fimc_is_hw_set_intgr0_gd0(is); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci return fimc_is_wait_event(is, IS_ST_OPEN_SENSOR, 1, 64962306a36Sopenharmony_ci sensor->drvdata->open_timeout); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ciint fimc_is_hw_initialize(struct fimc_is *is) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci static const int config_ids[] = { 65662306a36Sopenharmony_ci IS_SC_PREVIEW_STILL, IS_SC_PREVIEW_VIDEO, 65762306a36Sopenharmony_ci IS_SC_CAPTURE_STILL, IS_SC_CAPTURE_VIDEO 65862306a36Sopenharmony_ci }; 65962306a36Sopenharmony_ci struct device *dev = &is->pdev->dev; 66062306a36Sopenharmony_ci u32 prev_id; 66162306a36Sopenharmony_ci int i, ret; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Sensor initialization. Only one sensor is currently supported. */ 66462306a36Sopenharmony_ci ret = fimc_is_hw_open_sensor(is, &is->sensor[0]); 66562306a36Sopenharmony_ci if (ret < 0) 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Get the setfile address. */ 66962306a36Sopenharmony_ci fimc_is_hw_get_setfile_addr(is); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, 67262306a36Sopenharmony_ci FIMC_IS_CONFIG_TIMEOUT); 67362306a36Sopenharmony_ci if (ret < 0) { 67462306a36Sopenharmony_ci dev_err(dev, "get setfile address timed out\n"); 67562306a36Sopenharmony_ci return ret; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci pr_debug("setfile.base: %#x\n", is->setfile.base); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* Load the setfile. */ 68062306a36Sopenharmony_ci fimc_is_load_setfile(is, FIMC_IS_SETFILE_6A3); 68162306a36Sopenharmony_ci clear_bit(IS_ST_SETFILE_LOADED, &is->state); 68262306a36Sopenharmony_ci fimc_is_hw_load_setfile(is); 68362306a36Sopenharmony_ci ret = fimc_is_wait_event(is, IS_ST_SETFILE_LOADED, 1, 68462306a36Sopenharmony_ci FIMC_IS_CONFIG_TIMEOUT); 68562306a36Sopenharmony_ci if (ret < 0) { 68662306a36Sopenharmony_ci dev_err(dev, "loading setfile timed out\n"); 68762306a36Sopenharmony_ci return ret; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci pr_debug("setfile: base: %#x, size: %d\n", 69162306a36Sopenharmony_ci is->setfile.base, is->setfile.size); 69262306a36Sopenharmony_ci pr_info("FIMC-IS Setfile info: %s\n", is->fw.setfile_info); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Check magic number. */ 69562306a36Sopenharmony_ci if (is->is_p_region->shared[MAX_SHARED_COUNT - 1] != 69662306a36Sopenharmony_ci FIMC_IS_MAGIC_NUMBER) { 69762306a36Sopenharmony_ci dev_err(dev, "magic number error!\n"); 69862306a36Sopenharmony_ci return -EIO; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci pr_debug("shared region: %pad, parameter region: %pad\n", 70262306a36Sopenharmony_ci &is->memory.addr + FIMC_IS_SHARED_REGION_OFFSET, 70362306a36Sopenharmony_ci &is->is_dma_p_region); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci is->setfile.sub_index = 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Stream off. */ 70862306a36Sopenharmony_ci fimc_is_hw_stream_off(is); 70962306a36Sopenharmony_ci ret = fimc_is_wait_event(is, IS_ST_STREAM_OFF, 1, 71062306a36Sopenharmony_ci FIMC_IS_CONFIG_TIMEOUT); 71162306a36Sopenharmony_ci if (ret < 0) { 71262306a36Sopenharmony_ci dev_err(dev, "stream off timeout\n"); 71362306a36Sopenharmony_ci return ret; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* Preserve previous mode. */ 71762306a36Sopenharmony_ci prev_id = is->config_index; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* Set initial parameter values. */ 72062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(config_ids); i++) { 72162306a36Sopenharmony_ci is->config_index = config_ids[i]; 72262306a36Sopenharmony_ci fimc_is_set_initial_params(is); 72362306a36Sopenharmony_ci ret = fimc_is_itf_s_param(is, true); 72462306a36Sopenharmony_ci if (ret < 0) { 72562306a36Sopenharmony_ci is->config_index = prev_id; 72662306a36Sopenharmony_ci return ret; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci is->config_index = prev_id; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci set_bit(IS_ST_INIT_DONE, &is->state); 73262306a36Sopenharmony_ci dev_info(dev, "initialization sequence completed (%d)\n", 73362306a36Sopenharmony_ci is->config_index); 73462306a36Sopenharmony_ci return 0; 73562306a36Sopenharmony_ci} 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_cistatic int fimc_is_show(struct seq_file *s, void *data) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct fimc_is *is = s->private; 74062306a36Sopenharmony_ci const u8 *buf = is->memory.vaddr + FIMC_IS_DEBUG_REGION_OFFSET; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (is->memory.vaddr == NULL) { 74362306a36Sopenharmony_ci dev_err(&is->pdev->dev, "firmware memory is not initialized\n"); 74462306a36Sopenharmony_ci return -EIO; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci seq_printf(s, "%s\n", buf); 74862306a36Sopenharmony_ci return 0; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(fimc_is); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic void fimc_is_debugfs_remove(struct fimc_is *is) 75462306a36Sopenharmony_ci{ 75562306a36Sopenharmony_ci debugfs_remove_recursive(is->debugfs_entry); 75662306a36Sopenharmony_ci is->debugfs_entry = NULL; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic void fimc_is_debugfs_create(struct fimc_is *is) 76062306a36Sopenharmony_ci{ 76162306a36Sopenharmony_ci is->debugfs_entry = debugfs_create_dir("fimc_is", NULL); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci debugfs_create_file("fw_log", S_IRUGO, is->debugfs_entry, is, 76462306a36Sopenharmony_ci &fimc_is_fops); 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int fimc_is_runtime_resume(struct device *dev); 76862306a36Sopenharmony_cistatic int fimc_is_runtime_suspend(struct device *dev); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic int fimc_is_probe(struct platform_device *pdev) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct device *dev = &pdev->dev; 77362306a36Sopenharmony_ci struct fimc_is *is; 77462306a36Sopenharmony_ci struct resource res; 77562306a36Sopenharmony_ci struct device_node *node; 77662306a36Sopenharmony_ci int ret; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci is = devm_kzalloc(&pdev->dev, sizeof(*is), GFP_KERNEL); 77962306a36Sopenharmony_ci if (!is) 78062306a36Sopenharmony_ci return -ENOMEM; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci is->pdev = pdev; 78362306a36Sopenharmony_ci is->isp.pdev = pdev; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci init_waitqueue_head(&is->irq_queue); 78662306a36Sopenharmony_ci spin_lock_init(&is->slock); 78762306a36Sopenharmony_ci mutex_init(&is->lock); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = of_address_to_resource(dev->of_node, 0, &res); 79062306a36Sopenharmony_ci if (ret < 0) 79162306a36Sopenharmony_ci return ret; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci is->regs = devm_ioremap_resource(dev, &res); 79462306a36Sopenharmony_ci if (IS_ERR(is->regs)) 79562306a36Sopenharmony_ci return PTR_ERR(is->regs); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci node = of_get_child_by_name(dev->of_node, "pmu"); 79862306a36Sopenharmony_ci if (!node) 79962306a36Sopenharmony_ci return -ENODEV; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci is->pmu_regs = of_iomap(node, 0); 80262306a36Sopenharmony_ci of_node_put(node); 80362306a36Sopenharmony_ci if (!is->pmu_regs) 80462306a36Sopenharmony_ci return -ENOMEM; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci is->irq = irq_of_parse_and_map(dev->of_node, 0); 80762306a36Sopenharmony_ci if (!is->irq) { 80862306a36Sopenharmony_ci dev_err(dev, "no irq found\n"); 80962306a36Sopenharmony_ci ret = -EINVAL; 81062306a36Sopenharmony_ci goto err_iounmap; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = fimc_is_get_clocks(is); 81462306a36Sopenharmony_ci if (ret < 0) 81562306a36Sopenharmony_ci goto err_iounmap; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci platform_set_drvdata(pdev, is); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci ret = request_irq(is->irq, fimc_is_irq_handler, 0, dev_name(dev), is); 82062306a36Sopenharmony_ci if (ret < 0) { 82162306a36Sopenharmony_ci dev_err(dev, "irq request failed\n"); 82262306a36Sopenharmony_ci goto err_clk; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci pm_runtime_enable(dev); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (!pm_runtime_enabled(dev)) { 82762306a36Sopenharmony_ci ret = fimc_is_runtime_resume(dev); 82862306a36Sopenharmony_ci if (ret < 0) 82962306a36Sopenharmony_ci goto err_irq; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(dev); 83362306a36Sopenharmony_ci if (ret < 0) 83462306a36Sopenharmony_ci goto err_pm_disable; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci ret = devm_of_platform_populate(dev); 83962306a36Sopenharmony_ci if (ret < 0) 84062306a36Sopenharmony_ci goto err_pm; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * Register FIMC-IS V4L2 subdevs to this driver. The video nodes 84462306a36Sopenharmony_ci * will be created within the subdev's registered() callback. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci ret = fimc_is_register_subdevs(is); 84762306a36Sopenharmony_ci if (ret < 0) 84862306a36Sopenharmony_ci goto err_pm; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci fimc_is_debugfs_create(is); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ret = fimc_is_request_firmware(is, FIMC_IS_FW_FILENAME); 85362306a36Sopenharmony_ci if (ret < 0) 85462306a36Sopenharmony_ci goto err_dfs; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci pm_runtime_put_sync(dev); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci dev_dbg(dev, "FIMC-IS registered successfully\n"); 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_cierr_dfs: 86262306a36Sopenharmony_ci fimc_is_debugfs_remove(is); 86362306a36Sopenharmony_ci fimc_is_unregister_subdevs(is); 86462306a36Sopenharmony_cierr_pm: 86562306a36Sopenharmony_ci pm_runtime_put_noidle(dev); 86662306a36Sopenharmony_ci if (!pm_runtime_enabled(dev)) 86762306a36Sopenharmony_ci fimc_is_runtime_suspend(dev); 86862306a36Sopenharmony_cierr_pm_disable: 86962306a36Sopenharmony_ci pm_runtime_disable(dev); 87062306a36Sopenharmony_cierr_irq: 87162306a36Sopenharmony_ci free_irq(is->irq, is); 87262306a36Sopenharmony_cierr_clk: 87362306a36Sopenharmony_ci fimc_is_put_clocks(is); 87462306a36Sopenharmony_cierr_iounmap: 87562306a36Sopenharmony_ci iounmap(is->pmu_regs); 87662306a36Sopenharmony_ci return ret; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int fimc_is_runtime_resume(struct device *dev) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci struct fimc_is *is = dev_get_drvdata(dev); 88262306a36Sopenharmony_ci int ret; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci ret = fimc_is_setup_clocks(is); 88562306a36Sopenharmony_ci if (ret) 88662306a36Sopenharmony_ci return ret; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return fimc_is_enable_clocks(is); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int fimc_is_runtime_suspend(struct device *dev) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci struct fimc_is *is = dev_get_drvdata(dev); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci fimc_is_disable_clocks(is); 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 90062306a36Sopenharmony_cistatic int fimc_is_resume(struct device *dev) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci /* TODO: */ 90362306a36Sopenharmony_ci return 0; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int fimc_is_suspend(struct device *dev) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci struct fimc_is *is = dev_get_drvdata(dev); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci /* TODO: */ 91162306a36Sopenharmony_ci if (test_bit(IS_ST_A5_PWR_ON, &is->state)) 91262306a36Sopenharmony_ci return -EBUSY; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_cistatic void fimc_is_remove(struct platform_device *pdev) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 92162306a36Sopenharmony_ci struct fimc_is *is = dev_get_drvdata(dev); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci pm_runtime_disable(dev); 92462306a36Sopenharmony_ci pm_runtime_set_suspended(dev); 92562306a36Sopenharmony_ci if (!pm_runtime_status_suspended(dev)) 92662306a36Sopenharmony_ci fimc_is_runtime_suspend(dev); 92762306a36Sopenharmony_ci free_irq(is->irq, is); 92862306a36Sopenharmony_ci fimc_is_unregister_subdevs(is); 92962306a36Sopenharmony_ci vb2_dma_contig_clear_max_seg_size(dev); 93062306a36Sopenharmony_ci fimc_is_put_clocks(is); 93162306a36Sopenharmony_ci iounmap(is->pmu_regs); 93262306a36Sopenharmony_ci fimc_is_debugfs_remove(is); 93362306a36Sopenharmony_ci release_firmware(is->fw.f_w); 93462306a36Sopenharmony_ci fimc_is_free_cpu_memory(is); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic const struct of_device_id fimc_is_of_match[] = { 93862306a36Sopenharmony_ci { .compatible = "samsung,exynos4212-fimc-is" }, 93962306a36Sopenharmony_ci { /* sentinel */ }, 94062306a36Sopenharmony_ci}; 94162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, fimc_is_of_match); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic const struct dev_pm_ops fimc_is_pm_ops = { 94462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(fimc_is_suspend, fimc_is_resume) 94562306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(fimc_is_runtime_suspend, fimc_is_runtime_resume, 94662306a36Sopenharmony_ci NULL) 94762306a36Sopenharmony_ci}; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic struct platform_driver fimc_is_driver = { 95062306a36Sopenharmony_ci .probe = fimc_is_probe, 95162306a36Sopenharmony_ci .remove_new = fimc_is_remove, 95262306a36Sopenharmony_ci .driver = { 95362306a36Sopenharmony_ci .of_match_table = fimc_is_of_match, 95462306a36Sopenharmony_ci .name = FIMC_IS_DRV_NAME, 95562306a36Sopenharmony_ci .pm = &fimc_is_pm_ops, 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci}; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int fimc_is_module_init(void) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci int ret; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci ret = fimc_is_register_i2c_driver(); 96462306a36Sopenharmony_ci if (ret < 0) 96562306a36Sopenharmony_ci return ret; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci ret = platform_driver_register(&fimc_is_driver); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci if (ret < 0) 97062306a36Sopenharmony_ci fimc_is_unregister_i2c_driver(); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci return ret; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void fimc_is_module_exit(void) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci fimc_is_unregister_i2c_driver(); 97862306a36Sopenharmony_ci platform_driver_unregister(&fimc_is_driver); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cimodule_init(fimc_is_module_init); 98262306a36Sopenharmony_cimodule_exit(fimc_is_module_exit); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ciMODULE_ALIAS("platform:" FIMC_IS_DRV_NAME); 98562306a36Sopenharmony_ciMODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>"); 98662306a36Sopenharmony_ciMODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); 98762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 988