diff options
Diffstat (limited to 'drivers/media/sprd_scale/common/img_scale.c')
| -rw-r--r-- | drivers/media/sprd_scale/common/img_scale.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/drivers/media/sprd_scale/common/img_scale.c b/drivers/media/sprd_scale/common/img_scale.c new file mode 100644 index 00000000..0fa976a2 --- /dev/null +++ b/drivers/media/sprd_scale/common/img_scale.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <asm/uaccess.h> +#include <video/sprd_scale_k.h> +#include <linux/kthread.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/vmalloc.h> + +#include "img_scale.h" +#include "compat_img_scale.h" +#include "dcam_drv.h" + +#ifndef CONFIG_64BIT +#include <soc/sprd/hardware.h> +#endif + +#define SCALE_DEVICE_NAME "sprd_scale" +#define SCALE_TIMEOUT 5000/*ms*/ +#define SCALE_MINOR MISC_DYNAMIC_MINOR +#define SC_COEFF_BUF_SIZE (24 << 10) + +static void scale_k_irq(void *fd) +{ + struct scale_k_file *scale_file = (struct scale_k_file *)fd; + + if (!scale_file) { + printk("scale_k_irq error: hand is null"); + return; + } + + printk("sc done.\n"); + + up(&scale_file->scale_done_sem); +} + +static void scale_k_file_init(struct scale_k_file *fd, struct scale_k_private *scale_private) +{ + fd->scale_private = scale_private; + + sema_init(&fd->scale_done_sem, 0); + + fd->drv_private.scale_fd = (void*)fd; + fd->drv_private.path_info.coeff_addr = scale_private->coeff_addr; + + spin_lock_init(&fd->drv_private.scale_drv_lock); + sema_init(&fd->drv_private.path_info.done_sem, 0); +} + +static int scale_k_open(struct inode *node, struct file *file) +{ + int ret = 0; + struct scale_k_file *fd = NULL; + struct scale_k_private *scale_private = NULL; + struct miscdevice *md = file->private_data; + + if (!md) { + ret = -EFAULT; + printk("scale_k_open error: miscdevice is null \n"); + goto exit; + } + scale_private = (struct scale_k_private *)md->this_device->platform_data; + if (!scale_private) { + ret = -EFAULT; + printk("scale_k_open error: scale_private is null \n"); + goto exit; + } + + fd = vzalloc(sizeof(*fd)); + if (!fd) { + ret = -ENOMEM; + printk("scale_k_open error: alloc \n"); + goto exit; + } + fd->dn = md->this_device->of_node; + scale_k_file_init(fd, scale_private); + + file->private_data = fd; + + SCALE_TRACE("scale_k_open fd=%p ret=%d\n", fd, ret); + +exit: + return ret; +} + +ssize_t scale_k_read(struct file *file, char __user *u_data, size_t cnt, loff_t *cnt_ret) +{ + uint32_t rt_word[2]; + + (void)file; (void)cnt; (void)cnt_ret; + + if (cnt < sizeof(uint32_t)) { + printk("scale_k_read error: wrong size of u_data: %ld \n", cnt); + return -1; + } + + rt_word[0] = SCALE_LINE_BUF_LENGTH; + rt_word[1] = SCALE_SC_COEFF_MAX; + + return copy_to_user(u_data, (void*)rt_word, (uint32_t)(2 * sizeof(uint32_t))); +} + +static int scale_k_release(struct inode *node, struct file *file) +{ + struct scale_k_file *fd = NULL; + struct scale_k_private *scale_private = NULL; + + fd = file->private_data; + if (!fd) { + goto exit; + } + + scale_private = fd->scale_private; + if (!scale_private) { + goto fd_free; + } + + down(&scale_private->start_sem); + up(&scale_private->start_sem); + +fd_free: + vfree(fd); + fd = NULL; + file->private_data = NULL; + +exit: + SCALE_TRACE("scale_k_release\n"); + + return 0; +} + +static void sprd_img_print_reg(void) +{ + uint32_t* reg_buf = NULL; + uint32_t reg_buf_len = 0x400; + int ret; + uint32_t print_len = 0, print_cnt = 0; + reg_buf = (uint32_t*)vzalloc(reg_buf_len); + if (NULL == reg_buf) + return; + ret = dcam_read_registers(reg_buf, ®_buf_len); + if (ret) { + vfree(reg_buf); + return; + } + printk("dcam registers \n"); + while (print_len < reg_buf_len) { + printk("offset 0x%03x : 0x%08x, 0x%08x, 0x%08x, 0x%08x \n", + print_len, + reg_buf[print_cnt], + reg_buf[print_cnt+1], + reg_buf[print_cnt+2], + reg_buf[print_cnt+3]); + print_cnt += 4; + print_len += 16; + } + udelay(1); + vfree(reg_buf); + return; +} +static long scale_k_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct scale_k_private *scale_private; + struct scale_k_file *fd; + struct scale_frame_param_t frame_params; + struct scale_slice_param_t slice_params; + + fd = file->private_data; + if (!fd) { + ret = - EFAULT; + printk("scale_k_ioctl error: fd null \n"); + goto ioctl_exit; + } + + scale_private = fd->scale_private; + if (!scale_private) { + ret = -EFAULT; + printk("scale_k_ioctl erro: scale private null \n"); + goto ioctl_exit; + } + + switch (cmd) { + case SCALE_IO_START: + + down(&scale_private->start_sem); + + ret = scale_k_module_en(fd->dn); + if (unlikely(ret)) { + printk("rot_k_ioctl erro: scale_module_en\n"); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + ret = scale_k_isr_reg(scale_k_irq, &fd->drv_private); + if (unlikely(ret)) { + printk("rot_k_ioctl error: scale_k_isr_reg\n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + + } + + ret = copy_from_user(&frame_params, (struct scale_frame_param_t *)arg, sizeof(frame_params)); + if (ret) { + printk("rot_k_ioctl error: get frame param info \n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + ret = scale_k_start(&frame_params, &fd->drv_private.path_info); + if (ret) { + printk("rot_k_ioctl error: frame start \n"); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + goto ioctl_exit; + } + + break; + case SCALE_IO_DONE: + ret = down_timeout(&fd->scale_done_sem, msecs_to_jiffies(SCALE_TIMEOUT)); + if (ret) { + printk("scale_k_ioctl error: interruptible time out\n"); + sprd_img_print_reg(); + goto ioctl_out; + } + + scale_k_stop(); + + scale_k_module_dis(fd->dn); + + up(&scale_private->start_sem); + break; + case SCALE_IO_CONTINUE: + /*Caution: slice scale is not supported by current driver.Please do not use it*/ + ret = copy_from_user(&slice_params, (struct scale_slice_param_t *)arg, sizeof(slice_params)); + if (ret) { + printk("rot_k_ioctl error: get slice param info \n"); + goto ioctl_exit; + } + + ret = scale_k_continue(&slice_params, &fd->drv_private.path_info); + if (ret) { + printk("rot_k_ioctl error: continue \n"); + } + break; + + default: + break; + } + +ioctl_exit: + return ret; + +ioctl_out: + dcam_resize_end(); + scale_k_stop(); + scale_k_module_dis(fd->dn); + up(&scale_private->start_sem); + return ret; +} + +static struct file_operations scale_fops = { + .owner = THIS_MODULE, + .open = scale_k_open, + .read = scale_k_read, + .unlocked_ioctl = scale_k_ioctl, + .compat_ioctl = compat_scale_k_ioctl, + .release = scale_k_release, +}; + +static struct miscdevice scale_dev = { + .minor = SCALE_MINOR, + .name = SCALE_DEVICE_NAME, + .fops = &scale_fops, +}; + +int scale_k_probe(struct platform_device *pdev) +{ + int ret; + struct scale_k_private *scale_private; + + scale_private = devm_kzalloc(&pdev->dev, sizeof(*scale_private), GFP_KERNEL); + if (!scale_private) { + return -ENOMEM; + } + scale_private->coeff_addr = (void *)vzalloc(SC_COEFF_BUF_SIZE); + if (!scale_private->coeff_addr) { + devm_kfree(&pdev->dev, scale_private); + return -ENOMEM; + } + sema_init(&scale_private->start_sem, 1); + + platform_set_drvdata(pdev, scale_private); + + ret = misc_register(&scale_dev); + if (ret) { + printk("scale_k_probe error: ret=%d\n", ret); + ret = -EACCES; + goto probe_out; + } + + scale_dev.this_device->of_node = pdev->dev.of_node; + scale_dev.this_device->platform_data = (void *)scale_private; + + goto exit; + +probe_out: + vfree(scale_private->coeff_addr); + devm_kfree(&pdev->dev, scale_private); + platform_set_drvdata(pdev, NULL); +exit: + return 0; +} + +static int scale_k_remove(struct platform_device *pdev) +{ + struct scale_k_private *scale_private; + + scale_private = platform_get_drvdata(pdev); + + if (!scale_private) + goto remove_exit; + + misc_deregister(&scale_dev); + if (scale_private->coeff_addr) { + vfree(scale_private->coeff_addr); + } + devm_kfree(&pdev->dev, scale_private); + platform_set_drvdata(pdev, NULL); + +remove_exit: + return 0; +} + +static const struct of_device_id of_match_table_scale[] = { + { .compatible = "sprd,sprd_scale", }, + { }, +}; + +static struct platform_driver scale_driver = +{ + .probe = scale_k_probe, + .remove = scale_k_remove, + .driver = { + .owner = THIS_MODULE, + .name = SCALE_DEVICE_NAME, + .of_match_table = of_match_ptr(of_match_table_scale), + } +}; + +int __init scale_k_init(void) +{ + if (platform_driver_register(&scale_driver) != 0) { + printk("platform scale device register Failed \n"); + return -1; + } + + return 0; +} + +void scale_k_exit(void) +{ + platform_driver_unregister(&scale_driver); +} + +module_init(scale_k_init); +module_exit(scale_k_exit); +MODULE_DESCRIPTION("Sprd Scale Driver"); +MODULE_LICENSE("GPL"); |
