/* * Samsung Exynos5 SoC series FIMC-IS driver * * exynos5 fimc-is core functions * * Copyright (c) 2011 Samsung Electronics Co., Ltd * * 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. */ #include #include #include #include #include #include #include #include #include #if defined(CONFIG_BUSFREQ_OPP) && defined(CONFIG_CPU_EXYNOS5250) #include #endif #include #include #include #include #include #include #include #include #include #include "fimc-is-core.h" #include "fimc-is-helper.h" #include "fimc-is-param.h" #include "fimc-is-cmd.h" #include "fimc-is-regs.h" #include "fimc-is-err.h" #include "fimc-is-misc.h" extern struct class *camera_class; struct device *camera_front = NULL; /*sys/class/camera/rear*/ struct device *camera_rear = NULL; /*sys/class/camera/front*/ EXPORT_SYMBOL(camera_rear); #if defined(CONFIG_VIDEOBUF2_ION) static void *is_vb_cookie; void *buf_start; #endif static struct fimc_is_dev *to_fimc_is_dev_from_front_dev (struct fimc_is_front_dev *front_dev) { return container_of(front_dev, struct fimc_is_dev, front); } static struct fimc_is_sensor_dev *to_fimc_is_sensor_dev (struct v4l2_subdev *sdev) { return container_of(sdev, struct fimc_is_sensor_dev, sd); } static struct fimc_is_front_dev *to_fimc_is_front_dev(struct v4l2_subdev *sdev) { return container_of(sdev, struct fimc_is_front_dev, sd); } static struct fimc_is_back_dev *to_fimc_is_back_dev(struct v4l2_subdev *sdev) { return container_of(sdev, struct fimc_is_back_dev, sd); } static int fimc_is_sensor_s_stream(struct v4l2_subdev *sd, int enable) { dbg("%s\n", __func__); return 0; } #if defined(CONFIG_VIDEOBUF2_CMA_PHYS) void fimc_is_mem_cache_clean(const void *start_addr, unsigned long size) { unsigned long paddr; dmac_map_area(start_addr, size, DMA_TO_DEVICE); /* * virtual & phsical addrees mapped directly, so we can convert * the address just using offset */ paddr = __pa((unsigned long)start_addr); outer_clean_range(paddr, paddr + size); } void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size) { unsigned long paddr; paddr = __pa((unsigned long)start_addr); outer_inv_range(paddr, paddr + size); dmac_unmap_area(start_addr, size, DMA_FROM_DEVICE); } int fimc_is_init_mem(struct fimc_is_dev *dev) { struct cma_info mem_info; char cma_name[16]; int err; dbg("fimc_is_init_mem - ION\n"); sprintf(cma_name, "%s%d", "fimc_is", 0); err = cma_info(&mem_info, &dev->pdev->dev, 0); dbg("%s : [cma_info] start_addr : 0x%x, end_addr : 0x%x, " "total_size : 0x%x, free_size : 0x%x\n", __func__, mem_info.lower_bound, mem_info.upper_bound, mem_info.total_size, mem_info.free_size); if (err) { dev_err(&dev->pdev->dev, "%s: get cma info failed\n", __func__); return -EINVAL; } dev->mem.size = FIMC_IS_A5_MEM_SIZE; dev->mem.base = (dma_addr_t)cma_alloc (&dev->pdev->dev, cma_name, (size_t)dev->mem.size, 0); dev->is_p_region = (struct is_region *)(phys_to_virt(dev->mem.base + FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE)); memset((void *)dev->is_p_region, 0, (unsigned long)sizeof(struct is_region)); fimc_is_mem_cache_clean((void *)dev->is_p_region, FIMC_IS_REGION_SIZE+1); dbg("ctrl->mem.size = 0x%x\n", dev->mem.size); dbg("ctrl->mem.base = 0x%x\n", dev->mem.base); return 0; } #elif defined(CONFIG_VIDEOBUF2_ION) void fimc_is_mem_init_mem_cleanup(void *alloc_ctxes) { vb2_ion_destroy_context(alloc_ctxes); } void fimc_is_mem_resume(void *alloc_ctxes) { vb2_ion_attach_iommu(alloc_ctxes); } void fimc_is_mem_suspend(void *alloc_ctxes) { vb2_ion_detach_iommu(alloc_ctxes); } int fimc_is_cache_flush(struct fimc_is_dev *dev, const void *start_addr, unsigned long size) { vb2_ion_sync_for_device(dev->mem.fw_cookie, (unsigned long)start_addr - (unsigned long)dev->mem.kvaddr, size, DMA_BIDIRECTIONAL); return 0; } /* Allocate firmware */ int fimc_is_alloc_firmware(struct fimc_is_dev *dev) { void *fimc_is_bitproc_buf; int ret; dbg("Allocating memory for FIMC-IS firmware.\n"); fimc_is_bitproc_buf = vb2_ion_private_alloc(dev->alloc_ctx, FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF + SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF + SIZE_ISP_INTERNAL_BUF * NUM_ISP_INTERNAL_BUF); if (IS_ERR(fimc_is_bitproc_buf)) { fimc_is_bitproc_buf = 0; err("Allocating bitprocessor buffer failed\n"); return -ENOMEM; } ret = vb2_ion_dma_address(fimc_is_bitproc_buf, &dev->mem.dvaddr); if ((ret < 0) || (dev->mem.dvaddr & FIMC_IS_FW_BASE_MASK)) { err("The base memory is not aligned to 64MB.\n"); vb2_ion_private_free(fimc_is_bitproc_buf); dev->mem.dvaddr = 0; fimc_is_bitproc_buf = 0; return -EIO; } dbg("Device vaddr = %08x , size = %08x\n", dev->mem.dvaddr, FIMC_IS_A5_MEM_SIZE); dev->mem.kvaddr = vb2_ion_private_vaddr(fimc_is_bitproc_buf); if (IS_ERR(dev->mem.kvaddr)) { err("Bitprocessor memory remap failed\n"); vb2_ion_private_free(fimc_is_bitproc_buf); dev->mem.dvaddr = 0; fimc_is_bitproc_buf = 0; return -EIO; } dbg("Virtual address for FW: %08lx\n", (long unsigned int)dev->mem.kvaddr); dev->mem.bitproc_buf = fimc_is_bitproc_buf; dev->mem.fw_cookie = fimc_is_bitproc_buf; is_vb_cookie = dev->mem.fw_cookie; buf_start = dev->mem.kvaddr; return 0; } void fimc_is_mem_cache_clean(const void *start_addr, unsigned long size) { off_t offset; if (start_addr < buf_start) { err("Start address error\n"); return; } size--; offset = start_addr - buf_start; vb2_ion_sync_for_device(is_vb_cookie, offset, size, DMA_TO_DEVICE); } void fimc_is_mem_cache_inv(const void *start_addr, unsigned long size) { off_t offset; if (start_addr < buf_start) { err("Start address error\n"); return; } offset = start_addr - buf_start; vb2_ion_sync_for_device(is_vb_cookie, offset, size, DMA_FROM_DEVICE); } int fimc_is_init_mem(struct fimc_is_dev *dev) { int ret; dbg("fimc_is_init_mem - ION\n"); ret = fimc_is_alloc_firmware(dev); if (ret) { err("Couldn't alloc for FIMC-IS firmware\n"); return -EINVAL; } memset(dev->mem.kvaddr, 0, FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF + SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF + SIZE_ISP_INTERNAL_BUF * NUM_ISP_INTERNAL_BUF); dev->is_p_region = (struct is_region *)(dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE - FIMC_IS_REGION_SIZE); dev->is_shared_region = (struct is_share_region *)(dev->mem.kvaddr + FIMC_IS_SHARED_REGION_ADDR); dev->mem.dvaddr_odc = (unsigned char *)(dev->mem.dvaddr + FIMC_IS_A5_MEM_SIZE); dev->mem.kvaddr_odc = dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE; dev->mem.dvaddr_dis = (unsigned char *) (dev->mem.dvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF); dev->mem.kvaddr_dis = dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF; dev->mem.dvaddr_3dnr = (unsigned char *) (dev->mem.dvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF); dev->mem.kvaddr_3dnr = dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF; dev->mem.dvaddr_isp = (unsigned char *) (dev->mem.dvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF + SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF); dev->mem.kvaddr_isp = dev->mem.kvaddr + FIMC_IS_A5_MEM_SIZE + SIZE_ODC_INTERNAL_BUF * NUM_ODC_INTERNAL_BUF + SIZE_DIS_INTERNAL_BUF * NUM_DIS_INTERNAL_BUF + SIZE_3DNR_INTERNAL_BUF * NUM_3DNR_INTERNAL_BUF; dev->mem.dvaddr_shared = (unsigned char *)dev->mem.dvaddr + ((unsigned char *)&dev->is_p_region->shared[0] - dev->mem.kvaddr); dev->mem.kvaddr_shared = dev->mem.kvaddr + ((unsigned char *)&dev->is_p_region->shared[0] - dev->mem.kvaddr); if (fimc_is_cache_flush(dev, (void *)dev->is_p_region, IS_PARAM_SIZE)) { err("fimc_is_cache_flush-Err\n"); return -EINVAL; } dbg("fimc_is_init_mem3\n"); return 0; } #endif static int fimc_is_request_firmware(struct fimc_is_dev *dev) { int ret; struct firmware *fw_blob; ret = request_firmware((const struct firmware **)&fw_blob, FIMC_IS_FW, &dev->pdev->dev); if (ret) { dev_err(&dev->pdev->dev, "could not load firmware (err=%d)\n", ret); return -EINVAL; } #if defined(CONFIG_VIDEOBUF2_CMA_PHYS) memcpy((void *)phys_to_virt(dev->mem.base), fw_blob->data, fw_blob->size); fimc_is_mem_cache_clean((void *)phys_to_virt(dev->mem.base), fw_blob->size + 1); #elif defined(CONFIG_VIDEOBUF2_ION) if (dev->mem.bitproc_buf == 0) { err("failed to load FIMC-IS F/W\n"); } else { memcpy(dev->mem.kvaddr, fw_blob->data, fw_blob->size); fimc_is_mem_cache_clean( (void *)dev->mem.kvaddr, fw_blob->size + 1); } #endif dbg("FIMC_IS F/W loaded successfully - size:%d\n", fw_blob->size); release_firmware(fw_blob); return ret; } int fimc_is_load_fw(struct fimc_is_dev *dev) { int ret; dbg("%s\n", __func__); if (test_bit(IS_ST_IDLE, &dev->state)) { /* 1. Load IS firmware */ ret = fimc_is_request_firmware(dev); if (ret) { err("failed to fimc_is_request_firmware (%d)\n", ret); return -EINVAL; } set_bit(IS_ST_PWR_ON, &dev->state); /* 3. A5 power on */ clear_bit(IS_ST_FW_DOWNLOADED, &dev->state); fimc_is_hw_a5_power(dev, 1); ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_FW_DOWNLOADED, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { err("wait timeout A5 power on: %s\n", __func__); return -EINVAL; } dbg("fimc_is_load_fw end\n"); } else { dbg("IS FW was loaded before\n"); } return 0; } static int fimc_is_load_setfile(struct fimc_is_dev *dev) { int ret; struct firmware *fw_blob; ret = request_firmware((const struct firmware **)&fw_blob, FIMC_IS_SETFILE, &dev->pdev->dev); if (ret) { dev_err(&dev->pdev->dev, "could not load firmware (err=%d)\n", ret); return -EINVAL; } #if defined(CONFIG_VIDEOBUF2_CMA_PHYS) memcpy((void *)phys_to_virt(dev->mem.base + dev->setfile.base), fw_blob->data, fw_blob->size); fimc_is_mem_cache_clean( (void *)phys_to_virt(dev->mem.base + dev->setfile.base), fw_blob->size + 1); #elif defined(CONFIG_VIDEOBUF2_ION) if (dev->mem.bitproc_buf == 0) { err("failed to load setfile\n"); } else { memcpy((dev->mem.kvaddr + dev->setfile.base), fw_blob->data, fw_blob->size); fimc_is_mem_cache_clean( (void *)(dev->mem.kvaddr + dev->setfile.base), fw_blob->size + 1); } #endif dev->setfile.state = 1; dbg("FIMC_IS setfile loaded successfully - size:%d\n", fw_blob->size); release_firmware(fw_blob); dbg("A5 mem base = 0x%08x\n", dev->mem.base); dbg("Setfile base = 0x%08x\n", dev->setfile.base); return ret; } int fimc_is_init_set(struct fimc_is_dev *dev , u32 val) { int ret; struct flite_frame f_frame; #ifdef FIMC_IS_A5_DEBUG_ON unsigned long debug_device = 0; #endif fimc_is_hw_diable_wdt(dev); dev->sensor.sensor_type = val; dev->sensor.id_dual = 0; dev->setfile.sub_index = 0; dbg("fimc_is_init\n"); if (test_bit(IS_ST_FW_DOWNLOADED, &dev->state)) { /* Init sequence 1: Open sensor */ dbg("v4l2 : open sensor : %d\n", val); /* set mipi & fimclite */ f_frame.o_width = dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.width + 16; f_frame.o_height = dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.height + 12; f_frame.offs_h = 0; f_frame.offs_v = 0; f_frame.width = dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.width + 16; f_frame.height = dev->video[FIMC_IS_VIDEO_NUM_SCALERC].frame.height + 12; /*start mipi & fimclite*/ dbg("start fimclite(pos:%d) (port:%d)\n", dev->sensor.id_position, dev->pdata->sensor_info[dev->sensor.id_position]->flite_id); start_fimc_lite(dev->pdata-> sensor_info[dev->sensor.id_position]-> flite_id, &f_frame); mdelay(10); dbg("start mipi (pos:%d) (port:%d)\n", dev->sensor.id_position, dev->pdata-> sensor_info[dev->sensor.id_position]->csi_id); start_mipi_csi(dev->pdata-> sensor_info[dev->sensor.id_position]-> csi_id, &f_frame); clear_bit(IS_ST_OPEN_SENSOR, &dev->state); fimc_is_hw_open_sensor(dev, dev->sensor.id_dual, val); ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_OPEN_SENSOR, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { dev_err(&dev->pdev->dev, "wait timeout:%s\n", __func__); return -EBUSY; } if (dev->is_p_region->shared[MAX_SHARED_COUNT-1] != MAGIC_NUMBER) dev_err(&dev->pdev->dev, "MAGIC NUMBER error\n"); dbg("v4l2 : setfile address\n"); fimc_is_hw_get_setfile_addr(dev); ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_SETFILE_LOADED, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { err("wait timeout - get setfile address\n"); /*fimc_is_hw_set_low_poweroff(dev, true);*/ return -EINVAL; } fimc_is_load_setfile(dev); dbg(" fimc_is_load_setfile end\n"); clear_bit(IS_ST_SETFILE_LOADED, &dev->state); fimc_is_hw_load_setfile(dev); ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_SETFILE_LOADED, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { err("wait timeout - get setfile address\n"); /*fimc_is_hw_set_low_poweroff(dev, true);*/ return -EINVAL; } dbg("v4l2 : Load set file end\n"); /* Debug only */ dbg("Parameter region addr = 0x%08x\n", (unsigned int)(dev->is_p_region)); dev->frame_count = 0; dbg("Stream Off\n"); clear_bit(IS_ST_STREAM_OFF, &dev->state); fimc_is_hw_set_stream(dev, 0); /*stream off */ ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_STREAM_OFF, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { dev_err(&dev->pdev->dev, "wait timeout : %s\n", __func__); return -EBUSY; } clear_bit(IS_ST_RUN, &dev->state); /* 1. */ dbg("Default setting : preview_still\n"); dev->scenario_id = ISS_PREVIEW_STILL; fimc_is_hw_set_init(dev); fimc_is_hw_change_size(dev); fimc_is_mem_cache_clean((void *)dev->is_p_region, IS_PARAM_SIZE); /* 1 */ set_bit(IS_ST_INIT_PREVIEW_STILL, &dev->state); fimc_is_hw_set_param(dev); ret = wait_event_timeout(dev->irq_queue, test_bit(IS_ST_INIT_PREVIEW_VIDEO, &dev->state), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { dev_err(&dev->pdev->dev, "wait timeout : %s\n", __func__); return -EBUSY; } #ifdef FIMC_IS_A5_DEBUG_ON /*set_bit(FIMC_IS_DEBUG_MAIN, &debug_device); set_bit(FIMC_IS_DEBUG_EC, &debug_device); set_bit(FIMC_IS_DEBUG_SENSOR, &debug_device); set_bit(FIMC_IS_DEBUG_ISP, &debug_device); set_bit(FIMC_IS_DEBUG_DRC, &debug_device); set_bit(FIMC_IS_DEBUG_FD, &debug_device); set_bit(FIMC_IS_DEBUG_SDK, &debug_device); set_bit(FIMC_IS_DEBUG_SCALERC, &debug_device); set_bit(FIMC_IS_DEBUG_ODC, &debug_device); set_bit(FIMC_IS_DEBUG_TDNR, &debug_device); set_bit(FIMC_IS_DEBUG_SCALERP, &debug_device); set_bit(FIMC_IS_DEBUG_DIS, &debug_device); */ fimc_is_hw_set_debug_level(dev, FIMC_IS_DEBUG_UART, debug_device, FIMC_IS_DEBUG_LEVEL); #endif clear_bit(IS_ST_STREAM_OFF, &dev->state); set_bit(IS_ST_RUN, &dev->state); dbg("Init sequence completed!! Ready to use\n"); } return 0; } static int fimc_is_front_s_stream(struct v4l2_subdev *sd, int enable) { struct fimc_is_front_dev *front_dev = to_fimc_is_front_dev(sd); struct fimc_is_dev *isp = to_fimc_is_dev_from_front_dev(front_dev); int ret; if (enable) { printk(KERN_INFO "fimc_is_front_s_stream : ON\n"); } else { printk(KERN_INFO "fimc_is_front_s_stream : OFF\n"); fimc_is_hw_subip_poweroff(isp); ret = wait_event_timeout(isp->irq_queue, !test_bit(FIMC_IS_PWR_ST_POWER_ON_OFF, &isp->power), FIMC_IS_SHUTDOWN_TIMEOUT); if (!ret) { dev_err(&isp->pdev->dev, "wait timeout : %s\n", __func__); return -EBUSY; } fimc_is_hw_a5_power(isp, 0); isp->state = 0; isp->pipe_state = 0; stop_mipi_csi(isp->pdata->sensor_info[0]->csi_id); stop_mipi_csi(isp->pdata->sensor_info[1]->csi_id); stop_fimc_lite(isp->pdata->sensor_info[0]->flite_id); stop_fimc_lite(isp->pdata->sensor_info[1]->flite_id); if (isp->pdata->clk_off) { /* isp->pdata->clk_off(isp->pdev); */ } else { err("#### failed to Clock On ####\n"); return -EINVAL; } set_bit(IS_ST_IDLE , &isp->state); dbg("state(%d), pipe_state(%d)\n", (int)isp->state, (int)isp->pipe_state); } dbg("%s\n", __func__); return 0; } static int fimc_is_back_s_stream(struct v4l2_subdev *sd, int enable) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_format *fmt) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { dbg("%s\n", __func__); return 0; } static struct v4l2_subdev_pad_ops fimc_is_sensor_pad_ops = { .get_fmt = fimc_is_sensor_subdev_get_fmt, .set_fmt = fimc_is_sensor_subdev_set_fmt, .get_crop = fimc_is_sensor_subdev_get_crop, .set_crop = fimc_is_sensor_subdev_set_crop, }; static struct v4l2_subdev_pad_ops fimc_is_front_pad_ops = { .get_fmt = fimc_is_front_subdev_get_fmt, .set_fmt = fimc_is_front_subdev_set_fmt, .get_crop = fimc_is_front_subdev_get_crop, .set_crop = fimc_is_front_subdev_set_crop, }; static struct v4l2_subdev_pad_ops fimc_is_back_pad_ops = { .get_fmt = fimc_is_back_subdev_get_fmt, .set_fmt = fimc_is_back_subdev_set_fmt, .get_crop = fimc_is_back_subdev_get_crop, .set_crop = fimc_is_back_subdev_set_crop, }; static struct v4l2_subdev_video_ops fimc_is_sensor_video_ops = { .s_stream = fimc_is_sensor_s_stream, }; static struct v4l2_subdev_video_ops fimc_is_front_video_ops = { .s_stream = fimc_is_front_s_stream, }; static struct v4l2_subdev_video_ops fimc_is_back_video_ops = { .s_stream = fimc_is_back_s_stream, }; static struct v4l2_subdev_core_ops fimc_is_sensor_core_ops = { .s_ctrl = fimc_is_sensor_s_ctrl, .g_ctrl = fimc_is_sensor_g_ctrl, }; static struct v4l2_subdev_core_ops fimc_is_front_core_ops = { .s_ctrl = fimc_is_front_s_ctrl, .g_ctrl = fimc_is_front_g_ctrl, }; static struct v4l2_subdev_core_ops fimc_is_back_core_ops = { .s_ctrl = fimc_is_back_s_ctrl, .g_ctrl = fimc_is_back_g_ctrl, }; static struct v4l2_subdev_ops fimc_is_sensor_subdev_ops = { .pad = &fimc_is_sensor_pad_ops, .video = &fimc_is_sensor_video_ops, .core = &fimc_is_sensor_core_ops, }; static struct v4l2_subdev_ops fimc_is_front_subdev_ops = { .pad = &fimc_is_front_pad_ops, .video = &fimc_is_front_video_ops, .core = &fimc_is_front_core_ops, }; static struct v4l2_subdev_ops fimc_is_back_subdev_ops = { .pad = &fimc_is_back_pad_ops, .video = &fimc_is_back_video_ops, .core = &fimc_is_back_core_ops, }; static int fimc_is_sensor_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_sensor_subdev_registered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); return 0; } static void fimc_is_sensor_subdev_unregistered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); } static int fimc_is_front_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_front_subdev_registered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); return 0; } static void fimc_is_front_subdev_unregistered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); } static int fimc_is_back_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { dbg("%s\n", __func__); return 0; } static int fimc_is_back_subdev_registered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); return 0; } static void fimc_is_back_subdev_unregistered(struct v4l2_subdev *sd) { dbg("%s\n", __func__); } static const struct v4l2_subdev_internal_ops fimc_is_sensor_v4l2_internal_ops = { .open = fimc_is_sensor_init_formats, .close = fimc_is_sensor_subdev_close, .registered = fimc_is_sensor_subdev_registered, .unregistered = fimc_is_sensor_subdev_unregistered, }; static const struct v4l2_subdev_internal_ops fimc_is_front_v4l2_internal_ops = { .open = fimc_is_front_init_formats, .close = fimc_is_front_subdev_close, .registered = fimc_is_front_subdev_registered, .unregistered = fimc_is_front_subdev_unregistered, }; static const struct v4l2_subdev_internal_ops fimc_is_back_v4l2_internal_ops = { .open = fimc_is_back_init_formats, .close = fimc_is_back_subdev_close, .registered = fimc_is_back_subdev_registered, .unregistered = fimc_is_back_subdev_unregistered, }; static int fimc_is_sensor_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_is_sensor_dev *fimc_is_sensor = to_fimc_is_sensor_dev(sd); dbg("++%s\n", __func__); dbg("local->index : %d\n", local->index); dbg("media_entity_type(remote->entity) : %d\n", media_entity_type(remote->entity)); switch (local->index | media_entity_type(remote->entity)) { case FIMC_IS_SENSOR_PAD_SOURCE_FRONT | MEDIA_ENT_T_V4L2_SUBDEV: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_sensor->output = FIMC_IS_SENSOR_OUTPUT_FRONT; else fimc_is_sensor->output = FIMC_IS_SENSOR_OUTPUT_NONE; break; default: v4l2_err(sd, "%s : ERR link\n", __func__); return -EINVAL; } dbg("--%s\n", __func__); return 0; } static int fimc_is_front_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_is_front_dev *fimc_is_front = to_fimc_is_front_dev(sd); dbg("++%s\n", __func__); dbg("local->index : %d\n", local->index); switch (local->index | media_entity_type(remote->entity)) { case FIMC_IS_FRONT_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: dbg("fimc_is_front sink pad\n"); if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc_is_front->input != FIMC_IS_FRONT_INPUT_NONE) { dbg("BUSY\n"); return -EBUSY; } if (remote->index == FIMC_IS_SENSOR_PAD_SOURCE_FRONT) fimc_is_front->input = FIMC_IS_FRONT_INPUT_SENSOR; } else { fimc_is_front->input = FIMC_IS_FRONT_INPUT_NONE; } break; case FIMC_IS_FRONT_PAD_SOURCE_BACK | MEDIA_ENT_T_V4L2_SUBDEV: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_BACK; else fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE; break; case FIMC_IS_FRONT_PAD_SOURCE_BAYER | MEDIA_ENT_T_DEVNODE: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_BAYER; else fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE; break; case FIMC_IS_FRONT_PAD_SOURCE_SCALERC | MEDIA_ENT_T_DEVNODE: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_front->output |= FIMC_IS_FRONT_OUTPUT_SCALERC; else fimc_is_front->output = FIMC_IS_FRONT_OUTPUT_NONE; break; default: v4l2_err(sd, "%s : ERR link\n", __func__); return -EINVAL; } dbg("--%s\n", __func__); return 0; } static int fimc_is_back_link_setup(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags) { struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); struct fimc_is_back_dev *fimc_is_back = to_fimc_is_back_dev(sd); dbg("++%s\n", __func__); switch (local->index | media_entity_type(remote->entity)) { case FIMC_IS_BACK_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: dbg("fimc_is_back sink pad\n"); if (flags & MEDIA_LNK_FL_ENABLED) { if (fimc_is_back->input != FIMC_IS_BACK_INPUT_NONE) { dbg("BUSY\n"); return -EBUSY; } if (remote->index == FIMC_IS_FRONT_PAD_SOURCE_BACK) fimc_is_back->input = FIMC_IS_BACK_INPUT_FRONT; } else { fimc_is_back->input = FIMC_IS_FRONT_INPUT_NONE; } break; case FIMC_IS_BACK_PAD_SOURCE_3DNR | MEDIA_ENT_T_DEVNODE: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_back->output |= FIMC_IS_BACK_OUTPUT_3DNR; else fimc_is_back->output = FIMC_IS_FRONT_OUTPUT_NONE; break; case FIMC_IS_BACK_PAD_SOURCE_SCALERP | MEDIA_ENT_T_DEVNODE: if (flags & MEDIA_LNK_FL_ENABLED) fimc_is_back->output |= FIMC_IS_BACK_OUTPUT_SCALERP; else fimc_is_back->output = FIMC_IS_FRONT_OUTPUT_NONE; break; default: v4l2_err(sd, "%s : ERR link\n", __func__); return -EINVAL; } dbg("--%s\n", __func__); return 0; } static const struct media_entity_operations fimc_is_sensor_media_ops = { .link_setup = fimc_is_sensor_link_setup, }; static const struct media_entity_operations fimc_is_front_media_ops = { .link_setup = fimc_is_front_link_setup, }; static const struct media_entity_operations fimc_is_back_media_ops = { .link_setup = fimc_is_back_link_setup, }; int fimc_is_pipeline_s_stream_preview(struct media_entity *start_entity, int on) { struct media_pad *pad = &start_entity->pads[0]; struct v4l2_subdev *back_sd; struct v4l2_subdev *front_sd; struct v4l2_subdev *sensor_sd; int ret; dbg("--%s\n", __func__); pad = media_entity_remote_source(pad); if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV || pad == NULL) dbg("cannot find back entity\n"); back_sd = media_entity_to_v4l2_subdev(pad->entity); pad = &pad->entity->pads[0]; pad = media_entity_remote_source(pad); if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV || pad == NULL) dbg("cannot find front entity\n"); front_sd = media_entity_to_v4l2_subdev(pad->entity); pad = &pad->entity->pads[0]; pad = media_entity_remote_source(pad); if (media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV || pad == NULL) dbg("cannot find sensor entity\n"); sensor_sd = media_entity_to_v4l2_subdev(pad->entity); if (on) { ret = v4l2_subdev_call(sensor_sd, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; ret = v4l2_subdev_call(front_sd, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; ret = v4l2_subdev_call(back_sd, video, s_stream, 1); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; } else { ret = v4l2_subdev_call(back_sd, video, s_stream, 0); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; ret = v4l2_subdev_call(front_sd, video, s_stream, 0); if (ret < 0 && ret != -ENOIOCTLCMD) return ret; ret = v4l2_subdev_call(sensor_sd, video, s_stream, 0); } return ret == -ENOIOCTLCMD ? 0 : ret; } static int fimc_is_suspend(struct device *dev) { dbg("%s\n", __func__); return 0; } static int fimc_is_resume(struct device *dev) { dbg("%s\n", __func__); return 0; } static int fimc_is_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct fimc_is_dev *isp = (struct fimc_is_dev *)platform_get_drvdata(pdev); return fimc_is_hw_a5_power_off(isp); } static int fimc_is_runtime_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct fimc_is_dev *isp = (struct fimc_is_dev *)platform_get_drvdata(pdev); return fimc_is_hw_a5_power_on(isp); } static int fimc_is_get_md_callback(struct device *dev, void *p) { struct exynos_md **md_list = p; struct exynos_md *md = NULL; md = dev_get_drvdata(dev); if (md) *(md_list + md->id) = md; return 0; /* non-zero value stops iteration */ } static struct exynos_md *fimc_is_get_md(enum mdev_node node) { struct device_driver *drv; struct exynos_md *md[MDEV_MAX_NUM] = {NULL,}; int ret; drv = driver_find(MDEV_MODULE_NAME, &platform_bus_type); if (!drv) return ERR_PTR(-ENODEV); ret = driver_for_each_device(drv, NULL, &md[0], fimc_is_get_md_callback); put_driver(drv); return ret ? NULL : md[node]; } static unsigned int fimc_is_get_intr_position(unsigned int intr_status) { int i; for (i = 0; i < 5; i++) if (intr_status & (1<regs + INTSR1); intr_pos = fimc_is_get_intr_position(intr_status); if (intr_pos == INTR_GENERAL) { dev->i2h_cmd.cmd = readl(dev->regs + ISSR10); /* Read ISSR10 ~ ISSR15 */ switch (dev->i2h_cmd.cmd) { case IHC_GET_SENSOR_NUMBER: dbg("IHC_GET_SENSOR_NUMBER\n"); fimc_is_hw_get_param(dev, 1); dbg("ISP - FW version - %d\n", dev->i2h_cmd.arg[0]); dev->fw.ver = dev->i2h_cmd.arg[0]; fimc_is_hw_wait_intmsr0_intmsd0(dev); fimc_is_hw_set_sensor_num(dev); break; case IHC_SET_SHOT_MARK: fimc_is_hw_get_param(dev, 3); break; case IHC_SET_FACE_MARK: fimc_is_hw_get_param(dev, 2); break; case IHC_NOT_READY: break; case IHC_AA_DONE: fimc_is_hw_get_param(dev, 3); break; case ISR_DONE: fimc_is_hw_get_param(dev, 3); break; case ISR_NDONE: fimc_is_hw_get_param(dev, 4); /* fimc_is_fw_clear_insr1(dev); */ break; } /* Just clear the interrupt pending bits. */ fimc_is_fw_clear_irq1(dev, intr_pos); switch (dev->i2h_cmd.cmd) { case IHC_GET_SENSOR_NUMBER: fimc_is_hw_set_intgr0_gd0(dev); set_bit(IS_ST_FW_DOWNLOADED, &dev->state); break; case IHC_SET_SHOT_MARK: break; case IHC_SET_FACE_MARK: dbg("IHC_SET_FACE_MARK - %d, %d\n", dev->i2h_cmd.arg[0], dev->i2h_cmd.arg[1]); dev->fd_header.count = dev->i2h_cmd.arg[0]; dev->fd_header.index = dev->i2h_cmd.arg[1]; /* Implementation of AF with face */ if (dev->af.mode == IS_FOCUS_MODE_CONTINUOUS && dev->af.af_state == FIMC_IS_AF_LOCK) { fimc_is_af_face(dev); } else if (dev->af.mode == IS_FOCUS_MODE_FACEDETECT) { /* Using face information once only */ fimc_is_af_face(dev); dev->af.mode = IS_FOCUS_MODE_IDLE; } break; case IHC_AA_DONE: switch (dev->i2h_cmd.arg[0]) { /* SEARCH: Occurs when search is * requested at continuous AF */ case 2: break; /* INFOCUS: Occurs when focus is found. */ case 3: if (dev->af.af_state == FIMC_IS_AF_RUNNING) dev->af.af_state = FIMC_IS_AF_LOCK; dev->af.af_lock_state = 0x2; break; /* OUTOFFOCUS: Occurs when focus is not found. */ case 4: if (dev->af.af_state == FIMC_IS_AF_RUNNING) dev->af.af_state = FIMC_IS_AF_LOCK; dev->af.af_lock_state = 0x1; break; } break; case IHC_NOT_READY: err("Init Sequnce Error- IS will be turned off!!"); break; case ISR_DONE: dbg("ISR_DONE - %d\n", dev->i2h_cmd.arg[0]); switch (dev->i2h_cmd.arg[0]) { case HIC_PREVIEW_STILL: case HIC_PREVIEW_VIDEO: case HIC_CAPTURE_STILL: case HIC_CAPTURE_VIDEO: if (test_and_clear_bit(IS_ST_CHANGE_MODE, &dev->state)) { dev->sensor.offset_x = dev->i2h_cmd.arg[1]; dev->sensor.offset_y = dev->i2h_cmd.arg[2]; set_bit(IS_ST_CHANGE_MODE_DONE, &dev->state); } break; case HIC_STREAM_ON: clear_bit(IS_ST_CHANGE_MODE_DONE, &dev->state); set_bit(IS_ST_STREAM_ON, &dev->state); break; case HIC_STREAM_OFF: set_bit(IS_ST_STREAM_OFF, &dev->state); set_bit(IS_ST_RUN, &dev->state); break; case HIC_SET_PARAMETER: dev->p_region_index1 = 0; dev->p_region_index2 = 0; atomic_set(&dev->p_region_num, 0); /* FW bug - should be removed*/ if (dev->i2h_cmd.arg[1] == 0 && dev->i2h_cmd.arg[2] == 0) break; set_bit(IS_ST_BLOCK_CMD_CLEARED, &dev->state); if (test_bit(PARAM_ISP_AA, (void *)&dev->i2h_cmd.arg[1]) && (dev->af.af_state == FIMC_IS_AF_SETCONFIG)) { dev->af.af_state = FIMC_IS_AF_RUNNING; } else if (test_bit(PARAM_ISP_AA, (void *)&dev->i2h_cmd.arg[1]) && dev->af.af_state == FIMC_IS_AF_ABORT) { dev->af.af_state = FIMC_IS_AF_IDLE; } if (test_bit(IS_ST_INIT_PREVIEW_STILL, &dev->state)) set_bit(IS_ST_INIT_PREVIEW_VIDEO, &dev->state); else { clear_bit(IS_ST_SET_PARAM, &dev->state); set_bit(IS_ST_RUN, &dev->state); } break; case HIC_GET_PARAMETER: break; case HIC_SET_TUNE: break; case HIC_GET_STATUS: break; case HIC_OPEN_SENSOR: set_bit(IS_ST_OPEN_SENSOR, &dev->state); dbg("reply HIC_OPEN_SENSOR"); break; case HIC_CLOSE_SENSOR: clear_bit(IS_ST_OPEN_SENSOR, &dev->state); dev->sensor.id_dual = 0; break; case HIC_POWER_DOWN: clear_bit(FIMC_IS_PWR_ST_POWER_ON_OFF, &dev->power); break; case HIC_GET_SET_FILE_ADDR: dev->setfile.base = dev->i2h_cmd.arg[1]; set_bit(IS_ST_SETFILE_LOADED, &dev->state); break; case HIC_LOAD_SET_FILE: set_bit(IS_ST_SETFILE_LOADED, &dev->state); } break; case ISR_NDONE: err("ISR_NDONE - %d: %d\n", dev->i2h_cmd.arg[0], dev->i2h_cmd.arg[1]); switch (dev->i2h_cmd.arg[1]) { case IS_ERROR_SET_PARAMETER: fimc_is_mem_cache_inv((void *)dev->is_p_region, IS_PARAM_SIZE); break; } break; } } else if (intr_pos == INTR_FRAME_DONE_ISP) { dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR20); dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR21); dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR22); fimc_is_fw_clear_irq1(dev, intr_pos); buf_index = dev->i2h_cmd.arg[2]; dbg("Bayer returned buf index : %d\n", buf_index); vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_BAYER]. vbq.bufs[buf_index], VB2_BUF_STATE_DONE); fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_BAYER); } else if (intr_pos == INTR_FRAME_DONE_SCALERC) { dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR28); dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR29); dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR30); fimc_is_fw_clear_irq1(dev, intr_pos); buf_index = dev->i2h_cmd.arg[2]; dbg("ScalerC returned buf index : %d\n", buf_index); vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_SCALERC]. vbq.bufs[buf_index], VB2_BUF_STATE_DONE); fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_SCALERC); } else if (intr_pos == INTR_FRAME_DONE_TDNR) { dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR36); dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR37); dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR38); fimc_is_fw_clear_irq1(dev, intr_pos); buf_index = dev->i2h_cmd.arg[2]; dbg("3DNR returned buf index : %d\n", buf_index); vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_3DNR]. vbq.bufs[buf_index], VB2_BUF_STATE_DONE); fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_3DNR); } else if (intr_pos == INTR_FRAME_DONE_SCALERP) { dev->i2h_cmd.arg[0] = readl(dev->regs + ISSR44); dev->i2h_cmd.arg[1] = readl(dev->regs + ISSR45); dev->i2h_cmd.arg[2] = readl(dev->regs + ISSR46); fimc_is_fw_clear_irq1(dev, intr_pos); #ifdef DZOOM_EVT0 set_bit(IS_ST_SCALERP_FRAME_DONE, &dev->state); #endif buf_index = dev->i2h_cmd.arg[2]; dbg("ScalerP returned buf index : %d\n", buf_index); vb2_buffer_done(dev->video[FIMC_IS_VIDEO_NUM_SCALERP]. vbq.bufs[buf_index], VB2_BUF_STATE_DONE); fimc_is_hw_update_bufmask(dev, FIMC_IS_VIDEO_NUM_SCALERP); } wake_up(&dev->irq_queue); return IRQ_HANDLED; } static ssize_t s5k4e5_camera_rear_camtype_show(struct device *dev, struct device_attribute *attr, char *buf) { char type[] = "SLSI_S5K4E5_FIMC_IS"; return sprintf(buf, "%s\n", type); } static ssize_t s5k4e5_camera_rear_camfw_show(struct device *dev, struct device_attribute *attr, char *buf) { char type[] = "S5K4E5"; return sprintf(buf, "%s %s\n", type, type); } static ssize_t s5k6a3_camera_front_camtype_show(struct device *dev, struct device_attribute *attr, char *buf) { char type[] = "SLSI_S5K6A3_FIMC_IS"; return sprintf(buf, "%s\n", type); } static ssize_t s5k6a3_camera_front_camfw_show(struct device *dev, struct device_attribute *attr, char *buf) { char type[] = "S5K6A3"; return sprintf(buf, "%s %s\n", type, type); } static DEVICE_ATTR(rear_camtype, S_IWUSR|S_IWGRP|S_IROTH, s5k4e5_camera_rear_camtype_show, NULL); static DEVICE_ATTR(rear_camfw, S_IWUSR|S_IWGRP|S_IROTH, s5k4e5_camera_rear_camfw_show, NULL); static DEVICE_ATTR(front_camtype, S_IWUSR|S_IWGRP|S_IROTH, s5k6a3_camera_front_camtype_show, NULL); static DEVICE_ATTR(front_camfw, S_IWUSR|S_IWGRP|S_IROTH, s5k6a3_camera_front_camfw_show, NULL); static int fimc_is_probe(struct platform_device *pdev) { struct resource *mem_res; struct resource *regs_res; struct fimc_is_dev *isp; int ret = -ENODEV; struct vb2_queue *scalerc_q; struct vb2_queue *scalerp_q; struct vb2_queue *dnr_q; struct vb2_queue *bayer_q; dbg("fimc_is_front_probe\n"); isp = kzalloc(sizeof(struct fimc_is_dev), GFP_KERNEL); if (!isp) return -ENOMEM; isp->pdev = pdev; isp->pdata = pdev->dev.platform_data; isp->id = pdev->id; isp->pipe_state = 0; set_bit(FIMC_IS_STATE_IDLE, &isp->pipe_state); set_bit(IS_ST_IDLE , &isp->state); init_waitqueue_head(&isp->irq_queue); spin_lock_init(&isp->slock); mutex_init(&isp->vb_lock); mutex_init(&isp->lock); mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem_res) { dev_err(&pdev->dev, "Failed to get io memory region\n"); goto p_err1; } regs_res = request_mem_region(mem_res->start, resource_size(mem_res), pdev->name); if (!regs_res) { dev_err(&pdev->dev, "Failed to request io memory region\n"); goto p_err1; } isp->regs_res = regs_res; isp->regs = ioremap(mem_res->start, resource_size(mem_res)); if (!isp->regs) { dev_err(&pdev->dev, "Failed to remap io region\n"); goto p_err2; } isp->mdev = fimc_is_get_md(MDEV_ISP); if (IS_ERR_OR_NULL(isp->mdev)) goto p_err3; dbg("fimc_is_front->mdev : 0x%p\n", isp->mdev); #if defined(CONFIG_VIDEOBUF2_CMA_PHYS) isp->vb2 = &fimc_is_vb2_cma; #elif defined(CONFIG_VIDEOBUF2_ION) isp->vb2 = &fimc_is_vb2_ion; #endif isp->irq = platform_get_irq(pdev, 0); if (isp->irq < 0) { dev_err(&pdev->dev, "Failed to get irq\n"); goto p_err3; } ret = request_irq(isp->irq, fimc_is_irq_handler, 0, dev_name(&pdev->dev), isp); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); goto p_err3; } /*sensor entity*/ v4l2_subdev_init(&isp->sensor.sd, &fimc_is_sensor_subdev_ops); isp->sensor.sd.owner = THIS_MODULE; isp->sensor.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(isp->sensor.sd.name, sizeof(isp->sensor.sd.name), "%s\n", FIMC_IS_SENSOR_ENTITY_NAME); isp->sensor.pads.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&isp->sensor.sd.entity, 1, &isp->sensor.pads, 0); if (ret < 0) goto p_err3; fimc_is_sensor_init_formats(&isp->sensor.sd, NULL); isp->sensor.sd.internal_ops = &fimc_is_sensor_v4l2_internal_ops; isp->sensor.sd.entity.ops = &fimc_is_sensor_media_ops; ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev, &isp->sensor.sd); if (ret) goto p_err3; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&isp->sensor.sd, pdev); /*front entity*/ v4l2_subdev_init(&isp->front.sd, &fimc_is_front_subdev_ops); isp->front.sd.owner = THIS_MODULE; isp->front.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(isp->front.sd.name, sizeof(isp->front.sd.name), "%s\n", FIMC_IS_FRONT_ENTITY_NAME); isp->front.pads[FIMC_IS_FRONT_PAD_SINK].flags = MEDIA_PAD_FL_SINK; isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_BACK].flags = MEDIA_PAD_FL_SOURCE; isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_BAYER].flags = MEDIA_PAD_FL_SOURCE; isp->front.pads[FIMC_IS_FRONT_PAD_SOURCE_SCALERC].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&isp->front.sd.entity, FIMC_IS_FRONT_PADS_NUM, isp->front.pads, 0); if (ret < 0) goto p_err3; fimc_is_front_init_formats(&isp->front.sd, NULL); isp->front.sd.internal_ops = &fimc_is_front_v4l2_internal_ops; isp->front.sd.entity.ops = &fimc_is_front_media_ops; ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev, &isp->front.sd); if (ret) goto p_err3; /* This allows to retrieve the platform device id by the host driver */ v4l2_set_subdevdata(&isp->front.sd, pdev); /*back entity*/ v4l2_subdev_init(&isp->back.sd, &fimc_is_back_subdev_ops); isp->back.sd.owner = THIS_MODULE; isp->back.sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(isp->back.sd.name, sizeof(isp->back.sd.name), "%s\n", FIMC_IS_BACK_ENTITY_NAME); isp->back.pads[FIMC_IS_BACK_PAD_SINK].flags = MEDIA_PAD_FL_SINK; isp->back.pads[FIMC_IS_BACK_PAD_SOURCE_3DNR].flags = MEDIA_PAD_FL_SOURCE; isp->back.pads[FIMC_IS_BACK_PAD_SOURCE_SCALERP].flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_init(&isp->back.sd.entity, FIMC_IS_BACK_PADS_NUM, isp->back.pads, 0); if (ret < 0) goto p_err3; fimc_is_front_init_formats(&isp->back.sd, NULL); isp->back.sd.internal_ops = &fimc_is_back_v4l2_internal_ops; isp->back.sd.entity.ops = &fimc_is_back_media_ops; ret = v4l2_device_register_subdev(&isp->mdev->v4l2_dev, &isp->back.sd); if (ret) goto p_err3; v4l2_set_subdevdata(&isp->back.sd, pdev); /*front video entity - scalerC */ snprintf(isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.name, sizeof(isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.name), "%s", FIMC_IS_VIDEO_SCALERC_NAME); isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.fops = &fimc_is_scalerc_video_fops; isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.ioctl_ops = &fimc_is_scalerc_video_ioctl_ops; isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.v4l2_dev = &isp->mdev->v4l2_dev; isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.minor = -1; isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.release = video_device_release; isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.lock = &isp->vb_lock; video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd, isp); isp->video[FIMC_IS_VIDEO_NUM_SCALERC].dev = isp; scalerc_q = &isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vbq; memset(scalerc_q, 0, sizeof(*scalerc_q)); scalerc_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; scalerc_q->io_modes = VB2_MMAP | VB2_USERPTR; scalerc_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_SCALERC]; scalerc_q->ops = &fimc_is_scalerc_qops; scalerc_q->mem_ops = isp->vb2->ops; vb2_queue_init(scalerc_q); ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd, VFL_TYPE_GRABBER, FIMC_IS_VIDEO_NUM_SCALERC+EXYNOS_VIDEONODE_FIMC_IS); dbg("scalerC minor : %d\n", isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.minor); if (ret) { err("Failed to register ScalerC video device\n"); goto p_err3; } isp->video[FIMC_IS_VIDEO_NUM_SCALERC].pads.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_SCALERC]. vd.entity, 1, &isp->video[FIMC_IS_VIDEO_NUM_SCALERC].pads, 0); if (ret) { err("Failed to media_entity_init ScalerC video device\n"); goto p_err3; } /* back video entity - scalerP*/ snprintf(isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.name, sizeof(isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.name), "%s", FIMC_IS_VIDEO_SCALERP_NAME); isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.fops = &fimc_is_scalerp_video_fops; isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.ioctl_ops = &fimc_is_scalerp_video_ioctl_ops; isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.v4l2_dev = &isp->mdev->v4l2_dev; isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.minor = -1; isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.release = video_device_release; isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.lock = &isp->vb_lock; video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd, isp); isp->video[FIMC_IS_VIDEO_NUM_SCALERP].dev = isp; scalerp_q = &isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vbq; memset(scalerp_q, 0, sizeof(*scalerp_q)); scalerp_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; scalerp_q->io_modes = VB2_MMAP | VB2_USERPTR; scalerp_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_SCALERP]; scalerp_q->ops = &fimc_is_scalerp_qops; scalerp_q->mem_ops = isp->vb2->ops; vb2_queue_init(scalerp_q); ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd, VFL_TYPE_GRABBER, FIMC_IS_VIDEO_NUM_SCALERP + EXYNOS_VIDEONODE_FIMC_IS); dbg("scalerP minor : %d\n", isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.minor); if (ret) { err("Failed to register ScalerP video device\n"); goto p_err3; } isp->video[FIMC_IS_VIDEO_NUM_SCALERP].pads.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_SCALERP]. vd.entity, 1, &isp->video[FIMC_IS_VIDEO_NUM_SCALERP].pads, 0); if (ret) { err("Failed to media_entity_init ScalerP video device\n"); goto p_err3; } /*back video entity - 3DNR */ snprintf(isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.name, sizeof(isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.name), "%s", FIMC_IS_VIDEO_3DNR_NAME); isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.fops = &fimc_is_3dnr_video_fops; isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.ioctl_ops = &fimc_is_3dnr_video_ioctl_ops; isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.v4l2_dev = &isp->mdev->v4l2_dev; isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.minor = -1; isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.release = video_device_release; isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.lock = &isp->vb_lock; video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd, isp); isp->video[FIMC_IS_VIDEO_NUM_3DNR].dev = isp; dnr_q = &isp->video[FIMC_IS_VIDEO_NUM_3DNR].vbq; memset(dnr_q, 0, sizeof(*dnr_q)); dnr_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; dnr_q->io_modes = VB2_MMAP | VB2_USERPTR; dnr_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_3DNR]; dnr_q->ops = &fimc_is_3dnr_qops; dnr_q->mem_ops = isp->vb2->ops; vb2_queue_init(dnr_q); ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd, VFL_TYPE_GRABBER, FIMC_IS_VIDEO_NUM_3DNR + EXYNOS_VIDEONODE_FIMC_IS); dbg("3DNR minor : %d\n", isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.minor); if (ret) { err("Failed to register 3DNR video device\n"); goto p_err3; } isp->video[FIMC_IS_VIDEO_NUM_3DNR].pads.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.entity, 1, &isp->video[FIMC_IS_VIDEO_NUM_3DNR].pads, 0); if (ret) { err("Failed to media_entity_init 3DNR video device\n"); goto p_err3; } /* back video entity - bayer*/ snprintf(isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.name, sizeof(isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.name), "%s", FIMC_IS_VIDEO_BAYER_NAME); isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.fops = &fimc_is_bayer_video_fops; isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.ioctl_ops = &fimc_is_bayer_video_ioctl_ops; isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.v4l2_dev = &isp->mdev->v4l2_dev; isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.minor = -1; isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.release = video_device_release; isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.lock = &isp->vb_lock; video_set_drvdata(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd, isp); isp->video[FIMC_IS_VIDEO_NUM_BAYER].dev = isp; bayer_q = &isp->video[FIMC_IS_VIDEO_NUM_BAYER].vbq; memset(bayer_q, 0, sizeof(*bayer_q)); bayer_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; bayer_q->io_modes = VB2_MMAP | VB2_USERPTR; bayer_q->drv_priv = &isp->video[FIMC_IS_VIDEO_NUM_BAYER]; bayer_q->ops = &fimc_is_bayer_qops; bayer_q->mem_ops = isp->vb2->ops; vb2_queue_init(bayer_q); ret = video_register_device(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd, VFL_TYPE_GRABBER, FIMC_IS_VIDEO_NUM_BAYER + EXYNOS_VIDEONODE_FIMC_IS); dbg("scalerP minor : %d\n", isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.minor); if (ret) { err("Failed to register ScalerP video device\n"); goto p_err3; } isp->video[FIMC_IS_VIDEO_NUM_BAYER].pads.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&isp->video[FIMC_IS_VIDEO_NUM_BAYER].vd.entity, 1, &isp->video[FIMC_IS_VIDEO_NUM_BAYER].pads, 0); if (ret) { err("Failed to media_entity_init ScalerP video device\n"); goto p_err3; } platform_set_drvdata(pdev, isp); /* create link */ ret = media_entity_create_link( &isp->sensor.sd.entity, FIMC_IS_SENSOR_PAD_SOURCE_FRONT, &isp->front.sd.entity, FIMC_IS_FRONT_PAD_SINK, 0); if (ret < 0) { err("failed link creation from sensor to front\n"); goto p_err3; } ret = media_entity_create_link( &isp->front.sd.entity, FIMC_IS_FRONT_PAD_SOURCE_BACK, &isp->back.sd.entity, FIMC_IS_BACK_PAD_SINK, 0); if (ret < 0) { err("failed link creation from front to back\n"); goto p_err3; } ret = media_entity_create_link( &isp->front.sd.entity, FIMC_IS_FRONT_PAD_SOURCE_SCALERC, &isp->video[FIMC_IS_VIDEO_NUM_SCALERC].vd.entity, 0, 0); if (ret < 0) { err("failed link creation from front to scalerC video\n"); goto p_err3; } ret = media_entity_create_link( &isp->back.sd.entity, FIMC_IS_BACK_PAD_SOURCE_SCALERP, &isp->video[FIMC_IS_VIDEO_NUM_SCALERP].vd.entity, 0, 0); if (ret < 0) { err("failed link creation from back to scalerP video\n"); goto p_err3; } ret = media_entity_create_link( &isp->back.sd.entity, FIMC_IS_BACK_PAD_SOURCE_3DNR, &isp->video[FIMC_IS_VIDEO_NUM_3DNR].vd.entity, 0, 0); if (ret < 0) { err("failed link creation from back to 3DNR video\n"); goto p_err3; } /* register subdev nodes*/ ret = v4l2_device_register_subdev_nodes(&isp->mdev->v4l2_dev); if (ret) err("v4l2_device_register_subdev_nodes failed\n"); /* init vb2*/ isp->alloc_ctx = isp->vb2->init(isp); if (IS_ERR(isp->alloc_ctx)) { ret = PTR_ERR(isp->alloc_ctx); goto p_err1; } /* init memory*/ ret = fimc_is_init_mem(isp); if (ret) { dbg("failed to fimc_is_init_mem (%d)\n", ret); goto p_err3; } #if defined(CONFIG_BUSFREQ_OPP) && defined(CONFIG_CPU_EXYNOS5250) isp->bus_dev = dev_get("exynos-busfreq"); mutex_init(&isp->busfreq_lock); isp->busfreq_num = 0; #endif /*init gpio : should be moved to stream_on */ if (isp->pdata->cfg_gpio) { isp->pdata->cfg_gpio(isp->pdev); } else { dev_err(&isp->pdev->dev, "failed to init GPIO config\n"); goto p_err3; } #if defined(CONFIG_EXYNOS_DEV_PD) && defined(CONFIG_PM_RUNTIME) pm_runtime_enable(&pdev->dev); #endif if (camera_rear == NULL) camera_rear = device_create(camera_class, NULL, 0, NULL, "rear"); if (camera_front == NULL) camera_front = device_create(camera_class, NULL, 0, NULL, "front"); if (IS_ERR(camera_rear) || IS_ERR(camera_front)) { printk(KERN_ERR "failed to create device!\n"); } else { if (device_create_file(camera_front, &dev_attr_front_camtype) < 0) { printk(KERN_ERR "failed to create device file, %s\n", dev_attr_front_camtype.attr.name); } if (device_create_file(camera_front, &dev_attr_front_camfw) < 0) { printk(KERN_ERR "failed to create device file, %s\n", dev_attr_front_camfw.attr.name); } if (device_create_file(camera_rear, &dev_attr_rear_camtype) < 0) { printk(KERN_ERR "failed to create device file, %s\n", dev_attr_rear_camtype.attr.name); } if (device_create_file(camera_rear, &dev_attr_rear_camfw) < 0) { printk(KERN_ERR "failed to create device file, %s\n", dev_attr_rear_camfw.attr.name); } } dbg("%s : fimc_is_front_%d probe success\n", __func__, pdev->id); return 0; p_err3: iounmap(isp->regs); p_err2: release_mem_region(regs_res->start, resource_size(regs_res)); p_err1: kfree(isp); return ret; } static int fimc_is_remove(struct platform_device *pdev) { dbg("%s\n", __func__); return 0; } static const struct dev_pm_ops fimc_is_pm_ops = { .suspend = fimc_is_suspend, .resume = fimc_is_resume, .runtime_suspend = fimc_is_runtime_suspend, .runtime_resume = fimc_is_runtime_resume, }; static struct platform_driver fimc_is_driver = { .probe = fimc_is_probe, .remove = __devexit_p(fimc_is_remove), .driver = { .name = FIMC_IS_MODULE_NAME, .owner = THIS_MODULE, .pm = &fimc_is_pm_ops, } }; static int __init fimc_is_init(void) { int ret = platform_driver_register(&fimc_is_driver); if (ret) err("platform_driver_register failed: %d\n", ret); return ret; } static void __exit fimc_is_exit(void) { platform_driver_unregister(&fimc_is_driver); } module_init(fimc_is_init); module_exit(fimc_is_exit); MODULE_AUTHOR("Jiyoung Shin"); MODULE_DESCRIPTION("Exynos FIMC_IS front end driver"); MODULE_LICENSE("GPL");